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