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