001/* 002 * Copyright 2011-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-2024 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-2024 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 @NotNull 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(@NotNull 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(@NotNull 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 @Nullable() 174 public T get(@NotNull final T e) 175 { 176 final WeakReference<T> r = m.get(e); 177 if (r == null) 178 { 179 return null; 180 } 181 else 182 { 183 return r.get(); 184 } 185 } 186 187 188 189 /** 190 * Adds the provided element to this set, if it does not already exist. 191 * 192 * @param e The element to be added to the set if it does not already exist. 193 * 194 * @return {@code true} if the element was added to the set (because it was 195 * not already present), or {@code false} if the element was not 196 * added (because it was already in the set). 197 */ 198 @Override() 199 public boolean add(@NotNull final T e) 200 { 201 if (m.containsKey(e)) 202 { 203 return false; 204 } 205 else 206 { 207 m.put(e, new WeakReference<>(e)); 208 return true; 209 } 210 } 211 212 213 214 /** 215 * Adds any elements from the provided collection to this set if they were 216 * not already present. 217 * 218 * @param c The collection containing elements to add. 219 * 220 * @return {@code true} if at least one of the elements was not already in 221 * the set and was added, or {@code false} if no elements were added 222 * because they were already all present. 223 */ 224 @Override() 225 public boolean addAll(@NotNull final Collection<? extends T> c) 226 { 227 boolean changed = false; 228 for (final T e : c) 229 { 230 if (! m.containsKey(e)) 231 { 232 m.put(e, new WeakReference<>(e)); 233 changed = true; 234 } 235 } 236 237 return changed; 238 } 239 240 241 242 /** 243 * Adds the provided element to the set if it does not already exist, and 244 * retrieves the value stored in the set. 245 * 246 * @param e The element to be added to the set if it does not already exist. 247 * 248 * @return An existing version of the provided element if it was already in 249 * the set, or the provided object if it was just added. 250 */ 251 @Nullable() 252 public T addAndGet(@NotNull final T e) 253 { 254 final WeakReference<T> r = m.get(e); 255 if (r != null) 256 { 257 final T existingElement = r.get(); 258 if (existingElement != null) 259 { 260 return existingElement; 261 } 262 } 263 264 m.put(e, new WeakReference<>(e)); 265 return e; 266 } 267 268 269 270 /** 271 * Removes the specified element from this set, if it exists. 272 * 273 * @param e The element to be removed from this set. 274 * 275 * @return {@code true} if the element existed in the set and was removed, or 276 * {@code false} if not. 277 */ 278 @Override() 279 public boolean remove(@NotNull final Object e) 280 { 281 return (m.remove(e) != null); 282 } 283 284 285 286 /** 287 * Removes all of the elements of the provided collection from this set. 288 * 289 * @param c The collection containing the elements to remove from this set. 290 * 291 * @return {@code true} if at least one of the elements from the provided 292 * collection were contained in and therefore removed from the set, 293 * or {@code false} if none of the elements in the given collection 294 * were contained in this set. 295 */ 296 @Override() 297 public boolean removeAll(@NotNull final Collection<?> c) 298 { 299 boolean changed = false; 300 for (final Object o : c) 301 { 302 final Object e = m.remove(o); 303 if (e != null) 304 { 305 changed = true; 306 } 307 } 308 309 return changed; 310 } 311 312 313 314 /** 315 * Removes all elements from this set which are not contained in the provided 316 * collection. 317 * 318 * @param c The collection of elements to be retained. 319 * 320 * @return {@code true} if this set contained at least one element not in the 321 * provided collection that was therefore removed, or {@code false} 322 * if this set did not have any elements that were not in the 323 * provided collection. 324 */ 325 @Override() 326 public boolean retainAll(@NotNull final Collection<?> c) 327 { 328 boolean changed = false; 329 final Iterator<Map.Entry<T,WeakReference<T>>> iterator = 330 m.entrySet().iterator(); 331 while (iterator.hasNext()) 332 { 333 final Map.Entry<T,WeakReference<T>> e = iterator.next(); 334 if (! c.contains(e.getKey())) 335 { 336 iterator.remove(); 337 changed = true; 338 } 339 } 340 341 return changed; 342 } 343 344 345 346 /** 347 * Retrieves an iterator across all elements in this set. 348 * 349 * @return An iterator across all elements in this set. 350 */ 351 @Override() 352 @NotNull() 353 public Iterator<T> iterator() 354 { 355 return m.keySet().iterator(); 356 } 357 358 359 360 /** 361 * Retrieves an array containing all of the elements currently held in this 362 * set. 363 * 364 * @return An array containing all of the elements currently held in this 365 * set. 366 */ 367 @Override() 368 @NotNull() 369 public Object[] toArray() 370 { 371 return m.keySet().toArray(); 372 } 373 374 375 376 /** 377 * Retrieves an array containing all of the elements currently held in this 378 * set. 379 * 380 * @param a An array into which the elements will be added if there is 381 * sufficient space. 382 * 383 * @param <E> The type of element for the given array. 384 * 385 * @return The provided array (with the first {@code null} element depicting 386 * the end of the set elements if the given array is larger than this 387 * set), or a newly-allocated array if the provided array was not 388 * large enough. 389 */ 390 @Override() 391 @NotNull() 392 public <E> E[] toArray(@NotNull final E[] a) 393 { 394 return m.keySet().toArray(a); 395 } 396 397 398 399 /** 400 * Retrieves a hash code for this set. 401 * 402 * @return A hash code for this set. 403 */ 404 @Override() 405 public int hashCode() 406 { 407 return m.keySet().hashCode(); 408 } 409 410 411 412 /** 413 * Indicates whether the provided object is equal to this set. 414 * 415 * @param o The object for which to make the determination. 416 * 417 * @return {@code true} if the provided object is a non-{@code null} set with 418 * the same elements as this set, or {@code false} if not. 419 */ 420 @Override() 421 public boolean equals(@Nullable final Object o) 422 { 423 return ((o != null) && (o instanceof Set) && m.keySet().equals(o)); 424 } 425 426 427 428 /** 429 * Retrieves a string representation of this set. 430 * 431 * @return A string representation of this set. 432 */ 433 @Override() 434 @NotNull() 435 public String toString() 436 { 437 return m.keySet().toString(); 438 } 439}