001 /*
002 * Copyright 2011-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2011-2013 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 }