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