001/* 002 * Copyright 2011-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2011-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util; 022 023 024 025import java.lang.ref.WeakReference; 026import java.util.Collection; 027import java.util.Iterator; 028import java.util.Map; 029import java.util.Set; 030import java.util.WeakHashMap; 031 032 033 034/** 035 * This class provides a weak hash set, which maintains weak references to the 036 * elements it contains, so that they will be removed automatically once there 037 * are no more normal references to them. 038 * <BR><BR> 039 * Note that because this set uses weak references, elements may disappear from 040 * the set at any time without being explicitly removed. This means that care 041 * must be taken to ensure that the result of one method must not be considered 042 * authoritative for subsequent calls to the same method or other methods in 043 * this class. 044 * 045 * @param <T> The type of element held in this set. 046 */ 047@Mutable() 048@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 049public final class WeakHashSet<T> 050 implements Set<T> 051{ 052 // The map that will be used to provide the set implementation. 053 private final WeakHashMap<T,WeakReference<T>> m; 054 055 056 057 /** 058 * Creates a new weak hash set with the default initial capacity. 059 */ 060 public WeakHashSet() 061 { 062 m = new WeakHashMap<T,WeakReference<T>>(16); 063 } 064 065 066 067 /** 068 * Creates a new weak hash set with the specified initial capacity. 069 * 070 * @param initialCapacity The initial capacity for this weak hash set. It 071 * must not be {@code null}. 072 */ 073 public WeakHashSet(final int initialCapacity) 074 { 075 m = new WeakHashMap<T,WeakReference<T>>(initialCapacity); 076 } 077 078 079 080 /** 081 * Clears the contents of this set. 082 */ 083 public void clear() 084 { 085 m.clear(); 086 } 087 088 089 090 /** 091 * Indicates whether this set is currently empty. 092 * 093 * @return {@code true} if this set is empty, or {@code false} if not. 094 */ 095 public boolean isEmpty() 096 { 097 return m.isEmpty(); 098 } 099 100 101 102 /** 103 * Retrieves the number of elements currently held in this set. 104 * 105 * @return The number of elements currently held in this set. 106 */ 107 public int size() 108 { 109 return m.size(); 110 } 111 112 113 114 /** 115 * Indicates whether this set contains the specified element. 116 * 117 * @param e The element for which to make the determination. 118 * 119 * @return {@code true} if this set contains the specified element, or 120 * {@code false} if not. 121 */ 122 public boolean contains(final Object e) 123 { 124 return m.containsKey(e); 125 } 126 127 128 129 /** 130 * Indicates whether this set currently contains all of the elements in the 131 * provided collection. 132 * 133 * @param c The collection for which to make the determination. 134 * 135 * @return {@code true} if this set currently contains all of the elements in 136 * the provided collection, or {@code false} if not. 137 */ 138 public boolean containsAll(final Collection<?> c) 139 { 140 return m.keySet().containsAll(c); 141 } 142 143 144 145 /** 146 * Retrieves the existing instance of the provided element from this set. 147 * 148 * @param e The object for which to obtain the existing element. 149 * 150 * @return The existing instance of the provided element, or {@code null} if 151 * the provided element is not contained in this set. 152 */ 153 public T get(final T e) 154 { 155 final WeakReference<T> r = m.get(e); 156 if (r == null) 157 { 158 return null; 159 } 160 else 161 { 162 return r.get(); 163 } 164 } 165 166 167 168 /** 169 * Adds the provided element to this set, if it does not already exist. 170 * 171 * @param e The element to be added to the set if it does not already exist. 172 * 173 * @return {@code true} if the element was added to the set (because it was 174 * not already present), or {@code false} if the element was not 175 * added (because it was already in the set). 176 */ 177 public boolean add(final T e) 178 { 179 if (m.containsKey(e)) 180 { 181 return false; 182 } 183 else 184 { 185 m.put(e, new WeakReference<T>(e)); 186 return true; 187 } 188 } 189 190 191 192 /** 193 * Adds any elements from the provided collection to this set if they were 194 * not already present. 195 * 196 * @param c The collection containing elements to add. 197 * 198 * @return {@code true} if at least one of the elements was not already in 199 * the set and was added, or {@code false} if no elements were added 200 * because they were already all present. 201 */ 202 public boolean addAll(final Collection<? extends T> c) 203 { 204 boolean changed = false; 205 for (final T e : c) 206 { 207 if (! m.containsKey(e)) 208 { 209 m.put(e, new WeakReference<T>(e)); 210 changed = true; 211 } 212 } 213 214 return changed; 215 } 216 217 218 219 /** 220 * Adds the provided element to the set if it does not already exist, and 221 * retrieves the value stored in the set. 222 * 223 * @param e The element to be added to the set if it does not already exist. 224 * 225 * @return An existing version of the provided element if it was already in 226 * the set, or the provided object if it was just added. 227 */ 228 public T addAndGet(final T e) 229 { 230 final WeakReference<T> r = m.get(e); 231 if (r != null) 232 { 233 final T existingElement = r.get(); 234 if (existingElement != null) 235 { 236 return existingElement; 237 } 238 } 239 240 m.put(e, new WeakReference<T>(e)); 241 return e; 242 } 243 244 245 246 /** 247 * Removes the specified element from this set, if it exists. 248 * 249 * @param e The element to be removed from this set. 250 * 251 * @return {@code true} if the element existed in the set and was removed, or 252 * {@code false} if not. 253 */ 254 public boolean remove(final Object e) 255 { 256 return (m.remove(e) != null); 257 } 258 259 260 261 /** 262 * Removes all of the elements of the provided collection from this set. 263 * 264 * @param c The collection containing the elements to remove from this set. 265 * 266 * @return {@code true} if at least one of the elements from the provided 267 * collection were contained in and therefore removed from the set, 268 * or {@code false} if none of the elements in the given collection 269 * were contained in this set. 270 */ 271 public boolean removeAll(final Collection<?> c) 272 { 273 boolean changed = false; 274 for (final Object o : c) 275 { 276 final Object e = m.remove(o); 277 if (e != null) 278 { 279 changed = true; 280 } 281 } 282 283 return changed; 284 } 285 286 287 288 /** 289 * Removes all elements from this set which are not contained in the provided 290 * collection. 291 * 292 * @param c The collection of elements to be retained. 293 * 294 * @return {@code true} if this set contained at least one element not in the 295 * provided collection that was therefore removed, or {@code false} 296 * if this set did not have any elements that were not in the 297 * provided collection. 298 */ 299 public boolean retainAll(final Collection<?> c) 300 { 301 boolean changed = false; 302 final Iterator<Map.Entry<T,WeakReference<T>>> iterator = 303 m.entrySet().iterator(); 304 while (iterator.hasNext()) 305 { 306 final Map.Entry<T,WeakReference<T>> e = iterator.next(); 307 if (! c.contains(e.getKey())) 308 { 309 iterator.remove(); 310 changed = true; 311 } 312 } 313 314 return changed; 315 } 316 317 318 319 /** 320 * Retrieves an iterator across all elements in this set. 321 * 322 * @return An iterator across all elements in this set. 323 */ 324 public Iterator<T> iterator() 325 { 326 return m.keySet().iterator(); 327 } 328 329 330 331 /** 332 * Retrieves an array containing all of the elements currently held in this 333 * set. 334 * 335 * @return An array containing all of the elements currently held in this 336 * set. 337 */ 338 public Object[] toArray() 339 { 340 return m.keySet().toArray(); 341 } 342 343 344 345 /** 346 * Retrieves an array containing all of the elements currently held in this 347 * set. 348 * 349 * @param a An array into which the elements will be added if there is 350 * sufficient space. 351 * 352 * @param <E> The type of element for the given array. 353 * 354 * @return The provided array (with the first {@code null} element depicting 355 * the end of the set elements if the given array is larger than this 356 * set), or a newly-allocated array if the provided array was not 357 * large enough. 358 */ 359 public <E> E[] toArray(final E[] a) 360 { 361 return m.keySet().toArray(a); 362 } 363 364 365 366 /** 367 * Retrieves a hash code for this set. 368 * 369 * @return A hash code for this set. 370 */ 371 public int hashCode() 372 { 373 return m.keySet().hashCode(); 374 } 375 376 377 378 /** 379 * Indicates whether the provided object is equal to this set. 380 * 381 * @param o The object for which to make the determination. 382 * 383 * @return {@code true} if the provided object is a non-{@code null} set with 384 * the same elements as this set, or {@code false} if not. 385 */ 386 public boolean equals(final Object o) 387 { 388 return ((o != null) && (o instanceof Set) && m.keySet().equals(o)); 389 } 390 391 392 393 /** 394 * Retrieves a string representation of this set. 395 * 396 * @return A string representation of this set. 397 */ 398 @Override() 399 public String toString() 400 { 401 return m.keySet().toString(); 402 } 403}