Java/Collections Data Structure/Weak Set
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
A weak HashSet: element stored in the WeakHashSet might be garbage collected
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
/**
* A weak HashSet. An element stored in the WeakHashSet might be
* garbage collected, if there is no strong reference to this element.
*/
public class WeakHashSet extends HashSet {
/**
* Helps to detect garbage collected values.
*/
ReferenceQueue queue = new ReferenceQueue();
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set.
*/
public Iterator iterator() {
// remove garbage collected elements
processQueue();
// get an iterator of the superclass WeakHashSet
final Iterator i = super.iterator();
return new Iterator() {
public boolean hasNext() {
return i.hasNext();
}
public Object next() {
// unwrap the element
return getReferenceObject((WeakReference) i.next());
}
public void remove() {
// remove the element from the HashSet
i.remove();
}
};
}
/**
* Returns <code>true</code> if this set contains the specified element.
*
* @param o element whose presence in this set is to be tested.
* @return <code>true</code> if this set contains the specified element.
*/
public boolean contains(Object o) {
return super.contains(WeakElement.create(o));
}
/**
* Adds the specified element to this set if it is not already
* present.
*
* @param o element to be added to this set.
* @return <code>true</code> if the set did not already contain the specified
* element.
*/
public boolean add(Object o) {
processQueue();
return super.add(WeakElement.create(o, this.queue));
}
/**
* Removes the given element from this set if it is present.
*
* @param o object to be removed from this set, if present.
* @return <code>true</code> if the set contained the specified element.
*/
public boolean remove(Object o) {
boolean ret = super.remove(WeakElement.create(o));
processQueue();
return ret;
}
/**
* A convenience method to return the object held by the
* weak reference or <code>null</code> if it does not exist.
*/
private final Object getReferenceObject(WeakReference ref) {
return (ref == null) ? null : ref.get();
}
/**
* Removes all garbage collected values with their keys from the map.
* Since we don"t know how much the ReferenceQueue.poll() operation
* costs, we should call it only in the add() method.
*/
private final void processQueue() {
WeakElement wv = null;
while ((wv = (WeakElement) this.queue.poll()) != null) {
super.remove(wv);
}
}
/**
* A WeakHashSet stores objects of class WeakElement.
* A WeakElement wraps the element that should be stored in the WeakHashSet.
* WeakElement inherits from java.lang.ref.WeakReference.
* It redefines equals and hashCode which delegate to the corresponding methods
* of the wrapped element.
*/
static private class WeakElement extends WeakReference {
private int hash; /* Hashcode of key, stored here since the key
may be tossed by the GC */
private WeakElement(Object o) {
super(o);
hash = o.hashCode();
}
private WeakElement(Object o, ReferenceQueue q) {
super(o, q);
hash = o.hashCode();
}
private static WeakElement create(Object o) {
return (o == null) ? null : new WeakElement(o);
}
private static WeakElement create(Object o, ReferenceQueue q) {
return (o == null) ? null : new WeakElement(o, q);
}
/* A WeakElement is equal to another WeakElement iff they both refer to objects
that are, in turn, equal according to their own equals methods */
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof WeakElement))
return false;
Object t = this.get();
Object u = ((WeakElement) o).get();
if (t == u)
return true;
if ((t == null) || (u == null))
return false;
return t.equals(u);
}
public int hashCode() {
return hash;
}
}
}
Set which holds its members by using of WeakReferences.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/** Set which holds its members by using of WeakReferences.
* MT level: unsafe.
* <p><strong>Note:</strong> as of JDK 6.0 (b51), you can instead use
* <pre>
* Set<T> s = Collections.newSetFromMap(new WeakHashMap<T, Boolean>());
* </pre>
*
* @author Ales Novak
*/
public class WeakSet<E> extends AbstractSet<E> implements Cloneable, Serializable {
static final long serialVersionUID = 3062376055928236721L;
/** load factor */
private float loadFactor;
/** Number of items. */
private int size;
/** Modification count */
private long modcount;
/** Reference queue of collected weak refs */
private transient ReferenceQueue<E> refq;
/** Count of <tt>null</tt> in this set */
long nullCount;
/** An array of Entries */
private transient Entry<E>[] entries;
transient Entry<E> iterChain;
/** Constructs a new set. */
public WeakSet() {
this(11, 0.75f);
}
/** Constructs a new set containing the elements in the specified collection.
* @param c a collection to add
*/
public WeakSet(Collection<? extends E> c) {
this();
addAll(c);
}
/** Constructs a new, empty set;
* @param initialCapacity initial capacity
*/
public WeakSet(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/** Constructs a new, empty set;
*
* @param initialCapacity initial capacity
* @param loadFactor load factor
*/
public WeakSet(int initialCapacity, float loadFactor) {
if ((initialCapacity <= 0) || (loadFactor <= 0)) {
throw new IllegalArgumentException();
}
size = 0;
modcount = 0;
this.loadFactor = loadFactor;
nullCount = 0;
refq = new ReferenceQueue<E>();
entries = Entry.createArray(initialCapacity);
iterChain = null;
}
/**
* logs iterator chain (for debugging)
* @param msg
*/
void logIterChain(String msg) {
Logger log = Logger.getLogger(WeakSet.class.getName());
log.log(Level.FINE, msg);
if (iterChain == null) {
log.log(Level.FINE, "Empty");
return;
}
StringBuilder str = new StringBuilder();
Entry<E> it = iterChain;
str.append(size + ": ");
while (it != null) {
str.append(it.get() + "(" + it.hashcode + ")" + "->");
it = it.iterChainNext;
}
log.log(Level.FINE, str.toString());
}
/** Adds the specified element to this set if it is not already present.
*
* @param o an Object to add
*/
public boolean add(E o) {
if (o == null) {
size++;
nullCount++;
modcount++;
return true;
}
Entry e = object2Entry(o);
if (e != null) {
return false;
}
modcount++;
size++;
int hash = hashIt(o);
Entry<E> next = entries[hash];
iterChain = entries[hash] = new Entry<E>(this, o, refq, next, iterChain);
rehash();
return true;
}
/** Removes all of the elements from this set. */
public void clear() {
for (int i = 0; i < entries.length; i++) {
entries[i] = null;
}
nullCount = 0;
modcount++;
size = 0;
iterChain = null;
}
/** Returns a shallow copy of this WeakSet instance: the elements themselves are not cloned. */
public Object clone() {
WeakSet<E> nws = new WeakSet<E>(1, loadFactor);
nws.size = size;
nws.nullCount = nullCount;
Entry<E>[] cloned = Entry.createArray(entries.length);
nws.entries = cloned;
for (int i = 0; i < cloned.length; i++) {
Object ref;
if ((entries[i] == null) || ((ref = entries[i].get()) == null)) {
cloned[i] = null;
} else {
cloned[i] = ((entries[i] == null) ? null : entries[i].clone(nws.refq));
ref = null;
}
// chains into nws iterator chain
Entry<E> entry = cloned[i];
while (entry != null) {
entry.chainIntoIter(nws.iterChain);
nws.iterChain = entry;
entry = entry.next;
}
}
return nws;
}
/** Returns true if this set contains the specified element.
*
* @param o an Object to examine
*/
public boolean contains(Object o) {
if (o == null) {
return nullCount > 0;
}
return object2Entry(o) != null;
}
/** Returns true if this set contains no elements.
*/
public boolean isEmpty() {
return ((nullCount == 0) && (size() == 0));
}
/** Returns an iterator over the elements in this set. */
public Iterator<E> iterator() {
return new WeakSetIterator();
}
/** Removes the given element from this set if it is present.
*
* @param o an Object to remove
* @return <tt>true</tt> if and only if the Object was successfuly removed.
*/
public boolean remove(Object o) {
if (o == null) {
if (nullCount > 0) {
nullCount--;
modcount++;
size--;
}
return true;
}
Entry e = object2Entry(o);
if (e != null) {
modcount++;
size--;
e.remove();
rehash();
return true;
}
return false;
}
/** @return the number of elements in this set (its cardinality). */
public int size() {
checkRefQueue();
return size;
}
public <T> T[] toArray(T[] array) {
ArrayList<E> list = new ArrayList<E>(array.length);
Iterator<E> it = iterator();
while (it.hasNext()) {
list.add(it.next());
}
return list.toArray(array);
}
public Object[] toArray() {
ArrayList<E> list = new ArrayList<E>();
Iterator<E> it = iterator();
while (it.hasNext()) {
list.add(it.next());
}
return list.toArray();
}
// #14772
public String toString() {
StringBuffer buf = new StringBuffer();
Iterator e = iterator();
buf.append("[");
while (e.hasNext()) {
buf.append(String.valueOf(e.next()));
if (e.hasNext()) {
buf.append(", ");
}
}
buf.append("]");
return buf.toString();
}
/** Checks if the queue is empty if not pending weak refs are removed. */
void checkRefQueue() {
for (;;) {
Entry entry = Entry.class.cast(refq.poll());
if (entry == null) {
break;
}
entry.remove();
size--;
}
}
/** @return modcount */
long modCount() {
return modcount;
}
/** @return an index to entries array */
int hashIt(Object o) {
return (o.hashCode() & 0x7fffffff) % entries.length;
}
/** rehashes this Set */
void rehash() {
/*
float currentLF = ((float) size) / ((float) entries.length);
if (currentLF < loadFactor) {
return;
}
*/
}
/** @return an Entry with given object */
private Entry object2Entry(Object o) {
checkRefQueue(); // clear ref q
int hash = hashIt(o);
Entry e = entries[hash];
if (e == null) {
return null;
}
while ((e != null) && !e.equals(o)) {
e = e.next;
}
return e;
}
private void writeObject(ObjectOutputStream obtos)
throws IOException {
obtos.defaultWriteObject();
obtos.writeObject(toArray());
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream obtis) throws IOException, ClassNotFoundException {
obtis.defaultReadObject();
Object[] arr = (Object[]) obtis.readObject();
entries = new Entry[(int) (size * 1.5)];
refq = new ReferenceQueue<E>();
for (int i = 0; i < arr.length; i++) {
add((E)arr[i]);
}
}
class WeakSetIterator implements Iterator<E> {
Entry<E> current;
Entry<E> next;
E currentObj;
E nextObj;
final long myModcount;
long myNullCount;
WeakSetIterator() {
myModcount = modCount();
myNullCount = nullCount;
current = null;
next = null;
Entry<E> ee = iterChain;
if (ee == null) {
return;
}
E o = ee.get();
while (ee.isEnqueued()) {
ee = ee.iterChainNext;
if (ee == null) {
return;
}
o = ee.get();
}
nextObj = o;
next = ee;
}
public boolean hasNext() {
checkModcount();
return ((myNullCount > 0) || (next != null));
}
public E next() {
checkModcount();
checkRefQueue();
if (myNullCount > 0) {
myNullCount--;
return null;
} else {
if (next == null) {
throw new java.util.NoSuchElementException();
}
current = next;
currentObj = nextObj;
// move to next requested
do {
next = next.iterChainNext;
if (next == null) {
break;
}
nextObj = next.get();
} while (next.isEnqueued());
return currentObj;
}
}
public void remove() {
checkModcount();
if (current == null) {
throw new IllegalStateException();
}
current.remove();
size--;
}
void checkModcount() {
if (myModcount != modCount()) {
throw new ConcurrentModificationException();
}
}
}
/** Entries of this set */
static class Entry<E> extends WeakReference<E> {
/** reference to outer WeakSet */
private WeakSet<E> set;
// double linked list
Entry<E> prev;
Entry<E> next;
private final int hashcode;
Entry<E> iterChainNext;
Entry<E> iterChainPrev;
Entry(WeakSet<E> set, E referenced, ReferenceQueue<E> q, Entry<E> next, Entry<E> nextInIter) {
super(referenced, q);
this.set = set;
this.next = next;
this.prev = null;
if (next != null) {
next.prev = this;
}
if (referenced != null) {
hashcode = set.hashIt(referenced);
} else {
hashcode = 0;
}
chainIntoIter(nextInIter);
}
@SuppressWarnings("unchecked")
static final <E> Entry<E>[] createArray(int size) {
return new Entry[size];
}
void chainIntoIter(Entry<E> nextInIter) {
iterChainNext = nextInIter;
if (nextInIter != null) {
nextInIter.iterChainPrev = this;
}
}
/** deques itself */
void remove() {
if (prev != null) {
prev.next = next;
}
if (next != null) {
next.prev = prev;
}
if (iterChainNext != null) {
iterChainNext.iterChainPrev = iterChainPrev;
}
if (iterChainPrev != null) {
iterChainPrev.iterChainNext = iterChainNext;
} else { // root
set.iterChain = iterChainNext;
}
if (set.entries[hashcode] == this) {
set.entries[hashcode] = next;
}
prev = null;
next = null;
iterChainNext = null;
iterChainPrev = null;
}
public int hashCode() {
return hashcode;
}
public boolean equals(Object o) {
Object oo = get();
if (oo == null) {
return false;
} else {
return oo.equals(o);
}
}
public Entry<E> clone(ReferenceQueue<E> q) {
return new Entry<E>(set, get(), q, next != null ? next.clone(q) : null, null);
}
}
}
Weak HashSet
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* glassfish/bootstrap/legal/CDDLv1.0.txt or
* https://glassfish.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* HEADER in each file and include the License file at
* glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
* add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your
* own identifying information: Portions Copyright [yyyy]
* [name of copyright owner]
*/
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
/**
* A weak HashSet. An element stored in the WeakHashSet might be
* garbage collected, if there is no strong reference to this element.
*/
public class WeakHashSet extends HashSet {
/**
* Helps to detect garbage collected values.
*/
ReferenceQueue queue = new ReferenceQueue();
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set.
*/
public Iterator iterator() {
// remove garbage collected elements
processQueue();
// get an iterator of the superclass WeakHashSet
final Iterator i = super.iterator();
return new Iterator() {
public boolean hasNext() {
return i.hasNext();
}
public Object next() {
// unwrap the element
return getReferenceObject((WeakReference) i.next());
}
public void remove() {
// remove the element from the HashSet
i.remove();
}
};
}
/**
* Returns <code>true</code> if this set contains the specified element.
*
* @param o element whose presence in this set is to be tested.
* @return <code>true</code> if this set contains the specified element.
*/
public boolean contains(Object o) {
return super.contains(WeakElement.create(o));
}
/**
* Adds the specified element to this set if it is not already
* present.
*
* @param o element to be added to this set.
* @return <code>true</code> if the set did not already contain the specified
* element.
*/
public boolean add(Object o) {
processQueue();
return super.add(WeakElement.create(o, this.queue));
}
/**
* Removes the given element from this set if it is present.
*
* @param o object to be removed from this set, if present.
* @return <code>true</code> if the set contained the specified element.
*/
public boolean remove(Object o) {
boolean ret = super.remove(WeakElement.create(o));
processQueue();
return ret;
}
/**
* A convenience method to return the object held by the
* weak reference or <code>null</code> if it does not exist.
*/
private final Object getReferenceObject(WeakReference ref) {
return (ref == null) ? null : ref.get();
}
/**
* Removes all garbage collected values with their keys from the map.
* Since we don"t know how much the ReferenceQueue.poll() operation
* costs, we should call it only in the add() method.
*/
private final void processQueue() {
WeakElement wv = null;
while ((wv = (WeakElement) this.queue.poll()) != null) {
super.remove(wv);
}
}
/**
* A WeakHashSet stores objects of class WeakElement.
* A WeakElement wraps the element that should be stored in the WeakHashSet.
* WeakElement inherits from java.lang.ref.WeakReference.
* It redefines equals and hashCode which delegate to the corresponding methods
* of the wrapped element.
*/
static private class WeakElement extends WeakReference {
private int hash; /* Hashcode of key, stored here since the key
may be tossed by the GC */
private WeakElement(Object o) {
super(o);
hash = o.hashCode();
}
private WeakElement(Object o, ReferenceQueue q) {
super(o, q);
hash = o.hashCode();
}
private static WeakElement create(Object o) {
return (o == null) ? null : new WeakElement(o);
}
private static WeakElement create(Object o, ReferenceQueue q) {
return (o == null) ? null : new WeakElement(o, q);
}
/* A WeakElement is equal to another WeakElement iff they both refer to objects
that are, in turn, equal according to their own equals methods */
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof WeakElement))
return false;
Object t = this.get();
Object u = ((WeakElement) o).get();
if (t == u)
return true;
if ((t == null) || (u == null))
return false;
return t.equals(u);
}
public int hashCode() {
return hash;
}
}
}
Weak HashSet from objectweb jac
// Revised from objectweb jac
import java.lang.Cloneable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
public class WeakHashSet extends AbstractSet
implements Set
{
private transient WeakHashMap map;
public WeakHashSet() {
map = new WeakHashMap();
}
public WeakHashSet(Collection c) {
map = new WeakHashMap(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public WeakHashSet(int initialCapacity, float loadFactor) {
map = new WeakHashMap(initialCapacity, loadFactor);
}
public WeakHashSet(int initialCapacity) {
map = new WeakHashMap(initialCapacity);
}
public Iterator iterator() {
return map.keySet().iterator();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(Object o) {
return map.put(o, Boolean.TRUE)==null;
}
public boolean remove(Object o) {
return map.remove(o)==Boolean.TRUE;
}
public void clear() {
map.clear();
}
}
Weak HashSet from TJDO
/*
* Copyright 2004 (C) TJDO.
* All rights reserved.
*
* This software is distributed under the terms of the TJDO License version 1.0.
* See the terms of the TJDO License in the documentation provided with this software.
*
* $Id: WeakHashSet.java,v 1.1 2004/08/09 23:53:35 jackknifebarber Exp $
*/
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
/**
* A <tt>Set</tt> implementation with <em>weak elements</em>.
* This class implements the <tt>Set</tt> interface, backed by a hash table with
* weak keys (actually a <tt>WeakHashMap</tt> instance).
* An element in a <tt>WeakHashSet</tt> will automatically be removed when it
* is no longer in ordinary use.
* More precisely, the presence of an element will not prevent it from being
* discarded by the garbage collector, that is, made finalizable, finalized,
* and then reclaimed.
* When a element has been discarded it is effectively removed from the set,
* so this class behaves somewhat differently than other <tt>Set</tt>
* implementations.
* <p>
* The null element is supported.
* This class has performance characteristics similar to those of the
* <tt>HashSet</tt> class, and has the same efficiency parameters of
* <em>initial capacity</em> and <em>load factor</em>.
* <p>
* Like most collection classes, this class is not synchronized.
* A synchronized <tt>WeakHashSet</tt> may be constructed using the
* <tt>Collections.synchronizedSet</tt> method.
* <p>
* This class is intended primarily for use with objects whose
* <tt>equals</tt> methods test for object identity using the
* <tt>==</tt> operator.
* Once such an object is discarded it can never be recreated, so it is
* impossible to do a lookup of that key in a <tt>WeakHashSet</tt> at some later
* time and be surprised that its entry has been removed.
* This class will work perfectly well with objects whose <tt>equals</tt>
* methods are not based upon object identity, such as <tt>String</tt>
* instances.
* With such recreatable objects however, the automatic removal of
* <tt>WeakHashSet</tt> elements that have been discarded may prove to be
* confusing.
* <p>
* The behavior of the <tt>WeakHashSet</tt> class depends in part upon the
* actions of the garbage collector, so several familiar (though not required)
* <tt>Set</tt> invariants do not hold for this class.
* Because the garbage collector may discard elements at any time, a
* <tt>WeakHashSet</tt> may behave as though an unknown thread is silently
* removing elements.
* In particular, even if you synchronize on a <tt>WeakHashSet</tt> instance and
* invoke none of its mutator methods, it is possible for the <tt>size</tt>
* method to return smaller values over time, for the <tt>isEmpty</tt> method to
* return <tt>false</tt> and then <tt>true</tt>, for the <tt>contains</tt>
* method to return <tt>true</tt> and later <tt>false</tt> for a given object,
* for the <tt>add</tt> method to return <tt>true</tt> and the <tt>remove</tt>
* method to return <tt>false</tt> for an element that previously appeared to be
* in the set, and for successive examinations of the set to yield successively
* smaller numbers of elements.
* <p>
* Each element in a <tt>WeakHashSet</tt> is stored indirectly as the referent
* of a weak reference.
* Therefore an element will automatically be removed only after the weak
* references to it, both inside and outside of the set, have been cleared by
* the garbage collector.
* <p>
* The iterators returned by this class are <i>fail-fast</i>: if the set is
* structurally modified at any time after the iterator is created, in any way
* except through the iterator"s own <tt>remove</tt> or <tt>add</tt> methods,
* the iterator will throw a <tt>ConcurrentModificationException</tt>.
* Thus, in the face of concurrent modification, the iterator fails quickly and
* cleanly, rather than risking arbitrary, non-deterministic behavior at an
* undetermined time in the future.
* <p>
* Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification.
* Fail-fast iterators throw <tt>ConcurrentModificationException</tt> on a
* best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
*
*
* @author (borrowing
* liberally from java.util.HashSet)
* @version $Revision: 1.1 $
*/
public class WeakHashSet extends AbstractSet implements Set
{
/* Dummy value to associate with an Object in the backing Map. */
private static final Object PRESENT = new Object();
private final WeakHashMap map;
/**
* Constructs a new, empty set; the backing <tt>WeakHashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public WeakHashSet()
{
map = new WeakHashMap();
}
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>WeakHashMap</tt> is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @param c the collection whose elements are to be placed into this set.
* @throws NullPointerException if the specified collection is null.
*/
public WeakHashSet(Collection c)
{
map = new WeakHashMap(Math.max((int)(c.size() / .75f) + 1, 16));
addAll(c);
}
/**
* Constructs a new, empty set; the backing <tt>WeakHashMap</tt> instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map.
* @param loadFactor the load factor of the hash map.
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
public WeakHashSet(int initialCapacity, float loadFactor)
{
map = new WeakHashMap(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty set; the backing <tt>WeakHashMap</tt> instance has
* the specified initial capacity and default load factor, which is
* <tt>0.75</tt>.
*
* @param initialCapacity the initial capacity of the hash table.
* @throws IllegalArgumentException if the initial capacity is less
* than zero.
*/
public WeakHashSet(int initialCapacity)
{
map = new WeakHashMap(initialCapacity);
}
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set.
* @see "java.util.ConcurrentModificationException"
*/
public Iterator iterator()
{
return map.keySet().iterator();
}
/**
* Returns the number of elements in this set (its cardinality).
*
* @return the number of elements.
*/
public int size()
{
return map.size();
}
/**
* Indicates whether the set is empty.
*
* @return <code>true</code> if the set contains no elements.
*/
public boolean isEmpty()
{
return map.isEmpty();
}
/**
* Indicates whether the set contains the specified element.
*
* @param o the element to specify.
* @return <code>true</code> if the set contains the specified element.
*/
public boolean contains(Object o)
{
return map.containsKey(o);
}
/**
* Adds the specified element to the set if it is not already
* present.
*
* @param o the element to be added.
* @return <code>true</code> if the set did not already contain the specified
* element.
*/
public boolean add(Object o)
{
return map.put(o, PRESENT) == null;
}
/**
* Removes the specified element from the set if it is present.
*
* @param o the element to be removed.
* @return <code>true</code> if the set contained the specified element.
*/
public boolean remove(Object o)
{
return map.remove(o) == PRESENT;
}
/**
* Removes all of the elements from the set.
*/
public void clear()
{
map.clear();
}
}