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