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