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