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}