scolebourne 2004/03/31 16:07:48
Modified: collections RELEASE-NOTES.html
collections/src/test/org/apache/commons/collections/map
TestReferenceMap.java
collections/src/java/org/apache/commons/collections/map
ReferenceMap.java
Log:
Changed ReferenceMap to extend AbstractHashedMap, thus gaining a mapIterator() and
subclassability
Revision Changes Path
1.21 +1 -0 jakarta-commons/collections/RELEASE-NOTES.html
Index: RELEASE-NOTES.html
===================================================================
RCS file: /home/cvs/jakarta-commons/collections/RELEASE-NOTES.html,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- RELEASE-NOTES.html 31 Mar 2004 23:18:56 -0000 1.20
+++ RELEASE-NOTES.html 1 Apr 2004 00:07:48 -0000 1.21
@@ -30,6 +30,7 @@
<center><h3>ENHANCEMENTS</h3></center>
<ul>
+<li>ReferenceMap - Changed to extend AbstractHashedMap, thus gaining a
mapIterator() and subclassability</li>
<li>Fast3Map - Make Serializable [27946]</li>
<li>Fast3Map - Add clone() method</li>
<li>MultiKey - Add getKey(index) and size() methods and make constructor public</li>
1.4 +2 -2
jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestReferenceMap.java
Index: TestReferenceMap.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestReferenceMap.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- TestReferenceMap.java 18 Feb 2004 01:20:38 -0000 1.3
+++ TestReferenceMap.java 1 Apr 2004 00:07:48 -0000 1.4
@@ -29,7 +29,7 @@
*
* @author Paul Jack
*/
-public class TestReferenceMap extends AbstractTestMap {
+public class TestReferenceMap extends AbstractTestIterableMap {
public TestReferenceMap(String testName) {
super(testName);
1.11 +355 -434
jakarta-commons/collections/src/java/org/apache/commons/collections/map/ReferenceMap.java
Index: ReferenceMap.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/map/ReferenceMap.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- ReferenceMap.java 18 Feb 2004 01:13:19 -0000 1.10
+++ ReferenceMap.java 1 Apr 2004 00:07:48 -0000 1.11
@@ -22,57 +22,46 @@
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
-import java.util.AbstractCollection;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
-import org.apache.commons.collections.KeyValue;
import org.apache.commons.collections.keyvalue.DefaultMapEntry;
/**
- * Hash-based <code>Map</code> implementation that allows
- * mappings to be removed by the garbage collector.
+ * A <code>Map</code> implementation that allows mappings to be
+ * removed by the garbage collector.
* <p>
- * When you construct a <code>ReferenceMap</code>, you can
- * specify what kind of references are used to store the
- * map's keys and values. If non-hard references are
- * used, then the garbage collector can remove mappings
- * if a key or value becomes unreachable, or if the
- * JVM's memory is running low. For information on how
- * the different reference types behave, see
- * [EMAIL PROTECTED] Reference}.
+ * When you construct a <code>ReferenceMap</code>, you can specify what kind
+ * of references are used to store the map's keys and values.
+ * If non-hard references are used, then the garbage collector can remove
+ * mappings if a key or value becomes unreachable, or if the JVM's memory is
+ * running low. For information on how the different reference types behave,
+ * see [EMAIL PROTECTED] Reference}.
* <p>
- * Different types of references can be specified for keys
- * and values. The keys can be configured to be weak but
- * the values hard, in which case this class will behave
- * like a <a
href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
- * <code>WeakHashMap</code></a>. However, you
- * can also specify hard keys and weak values, or any other
- * combination. The default constructor uses hard keys
- * and soft values, providing a memory-sensitive cache.
+ * Different types of references can be specified for keys and values.
+ * The keys can be configured to be weak but the values hard,
+ * in which case this class will behave like a
+ * <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
+ * <code>WeakHashMap</code></a>. However, you can also specify hard keys and
+ * weak values, or any other combination. The default constructor uses
+ * hard keys and soft values, providing a memory-sensitive cache.
* <p>
- * The algorithms used are basically the same as those
- * in [EMAIL PROTECTED] java.util.HashMap}. In particular, you
- * can specify a load factor and capacity to suit your
- * needs. All optional [EMAIL PROTECTED] Map} operations are
- * supported.
+ * This [EMAIL PROTECTED] Map} implementation does <i>not</i> allow null elements.
+ * Attempting to add a null key or value to the map will raise a
<code>NullPointerException</code>.
* <p>
- * However, this [EMAIL PROTECTED] Map} implementation does <I>not</I>
- * allow null elements. Attempting to add a null key or
- * or a null value to the map will raise a
- * <code>NullPointerException</code>.
- * <p>
- * As usual, this implementation is not synchronized. You
- * can use [EMAIL PROTECTED] java.util.Collections#synchronizedMap} to
+ * As usual, this implementation is not synchronized.
+ * You can use [EMAIL PROTECTED] java.util.Collections#synchronizedMap} to
* provide synchronized access to a <code>ReferenceMap</code>.
+ * <p>
+ * NOTE: As from Commons Collections 3.1 this map extends
<code>AbstractHashedMap</code>
+ * (previously it extended AbstractMap). As a result, the implementation is now
+ * extensible and provides a <code>MapIterator</code>.
*
* @see java.lang.ref.Reference
*
@@ -80,8 +69,9 @@
* @version $Revision$ $Date$
*
* @author Paul Jack
+ * @author Stephen Colebourne
*/
-public class ReferenceMap extends AbstractMap {
+public class ReferenceMap extends AbstractHashedMap {
/**
* For serialization.
@@ -144,42 +134,6 @@
private transient ReferenceQueue queue = new ReferenceQueue();
/**
- * The hash table. Its length is always a power of two.
- */
- private transient Entry[] table;
-
- /**
- * Number of mappings in this map.
- */
- private transient int size;
-
- /**
- * When size reaches threshold, the map is resized.
- * See resize().
- */
- private transient int threshold;
-
- /**
- * Number of times this map has been modified.
- */
- private transient volatile int modCount;
-
- /**
- * Cached key set. May be null if key set is never accessed.
- */
- private transient Set keySet;
-
- /**
- * Cached entry set. May be null if entry set is never accessed.
- */
- private transient Set entrySet;
-
- /**
- * Cached values. May be null if values() is never accessed.
- */
- private transient Collection values;
-
- /**
* Constructs a new <code>ReferenceMap</code> that will
* use hard references to keys and soft references to values.
*/
@@ -230,12 +184,8 @@
* @param purgeValues should the value be automatically purged when the
* key is garbage collected
*/
- public ReferenceMap(
- int keyType,
- int valueType,
- int capacity,
- float loadFactor,
- boolean purgeValues) {
+ public ReferenceMap(int keyType, int valueType, int capacity,
+ float loadFactor, boolean purgeValues) {
this(keyType, valueType, capacity, loadFactor);
this.purgeValues = purgeValues;
}
@@ -253,39 +203,27 @@
* @param loadFactor the load factor for the map
*/
public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor)
{
- super();
+ super(capacity, loadFactor);
verify("keyType", keyType);
verify("valueType", valueType);
-
- if (capacity <= 0) {
- throw new IllegalArgumentException("capacity must be positive");
- }
- if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f)) {
- throw new IllegalArgumentException("Load factor must be greater than 0
and less than 1.");
- }
-
this.keyType = keyType;
this.valueType = valueType;
-
- int v = 1;
- while (v < capacity) v *= 2;
-
- this.table = new Entry[v];
- this.loadFactor = loadFactor;
- this.threshold = (int)(v * loadFactor);
}
-
- // used by constructor
+ /**
+ * Checks the type int is a valid value.
+ *
+ * @param name the name for error messages
+ * @param type the type value to check
+ * @throws IllegalArgumentException if the value if invalid
+ */
private static void verify(String name, int type) {
if ((type < HARD) || (type > WEAK)) {
- throw new IllegalArgumentException(name +
- " must be HARD, SOFT, WEAK.");
+ throw new IllegalArgumentException(name + " must be HARD, SOFT, WEAK.");
}
}
-
/**
* Writes this object to the given output stream.
*
@@ -294,7 +232,7 @@
*/
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
- out.writeInt(table.length);
+ out.writeInt(data.length);
// Have to use null-terminated list because size might shrink
// during iteration
@@ -317,8 +255,8 @@
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
- table = new Entry[in.readInt()];
- threshold = (int)(table.length * loadFactor);
+ data = new HashEntry[in.readInt()];
+ threshold = calculateThreshold(data.length, loadFactor);
queue = new ReferenceQueue();
Object key = in.readObject();
while (key != null) {
@@ -328,106 +266,55 @@
}
}
-
- /**
- * Constructs a reference of the given type to the given
- * referent. The reference is registered with the queue
- * for later purging.
- *
- * @param type HARD, SOFT or WEAK
- * @param referent the object to refer to
- * @param hash the hash code of the <I>key</I> of the mapping;
- * this number might be different from referent.hashCode() if
- * the referent represents a value and not a key
- */
- private Object toReference(int type, Object referent, int hash) {
- switch (type) {
- case HARD: return referent;
- case SOFT: return new SoftRef(hash, referent, queue);
- case WEAK: return new WeakRef(hash, referent, queue);
- default: throw new Error();
- }
- }
-
-
/**
- * Returns the entry associated with the given key.
- *
- * @param key the key of the entry to look up
- * @return the entry associated with that key, or null
- * if the key is not in this map
+ * Gets the entry mapped to the key specified.
+ *
+ * @param key the key
+ * @return the entry, null if no match
+ * @since Commons Collections 3.1
*/
- private Entry getEntry(Object key) {
+ protected HashEntry getEntry(Object key) {
if (key == null) {
return null;
+ } else {
+ return super.getEntry(key);
}
- int hash = key.hashCode();
- int index = indexFor(hash);
- for (Entry entry = table[index]; entry != null; entry = entry.next) {
- if ((entry.hash == hash) && key.equals(entry.getKey())) {
- return entry;
- }
- }
- return null;
}
-
+ //-----------------------------------------------------------------------
/**
- * Converts the given hash code into an index into the
- * hash table.
+ * Purges stale mappings from this map before read operations.
+ * <p>
+ * This implementation calls [EMAIL PROTECTED] #purge()} to maintain a
consistent state.
+ *
+ * @since Commons Collections 3.1
*/
- private int indexFor(int hash) {
- // mix the bits to avoid bucket collisions...
- hash += ~(hash << 15);
- hash ^= (hash >>> 10);
- hash += (hash << 3);
- hash ^= (hash >>> 6);
- hash += ~(hash << 11);
- hash ^= (hash >>> 16);
- return hash & (table.length - 1);
+ protected void purgeBeforeRead() {
+ purge();
}
-
-
/**
- * Resizes this hash table by doubling its capacity.
- * This is an expensive operation, as entries must
- * be copied from the old smaller table to the new
- * bigger table.
+ * Purges stale mappings from this map before write operations.
+ * <p>
+ * This implementation calls [EMAIL PROTECTED] #purge()} to maintain a
consistent state.
+ *
+ * @since Commons Collections 3.1
*/
- private void resize() {
- Entry[] old = table;
- table = new Entry[old.length * 2];
-
- for (int i = 0; i < old.length; i++) {
- Entry next = old[i];
- while (next != null) {
- Entry entry = next;
- next = next.next;
- int index = indexFor(entry.hash);
- entry.next = table[index];
- table[index] = entry;
- }
- old[i] = null;
- }
- threshold = (int)(table.length * loadFactor);
+ protected void purgeBeforeWrite() {
+ purge();
}
-
-
/**
* Purges stale mappings from this map.
* <p>
- * Ordinarily, stale mappings are only removed during
- * a write operation, although this method is called for both
- * read and write operations to maintain a consistent state.
- * <p>
* Note that this method is not synchronized! Special
* care must be taken if, for instance, you want stale
* mappings to be removed on a periodic basis by some
* background thread.
+ *
+ * @since Commons Collections 3.1
*/
- private void purge() {
+ protected void purge() {
Reference ref = queue.poll();
while (ref != null) {
purge(ref);
@@ -435,19 +322,18 @@
}
}
-
private void purge(Reference ref) {
// The hashCode of the reference is the hashCode of the
// mapping key, even if the reference refers to the
// mapping value...
int hash = ref.hashCode();
- int index = indexFor(hash);
- Entry previous = null;
- Entry entry = table[index];
+ int index = hashIndex(hash, data.length);
+ HashEntry previous = null;
+ HashEntry entry = data[index];
while (entry != null) {
- if (entry.purge(ref)) {
+ if (((ReferenceEntry) entry).purge(ref)) {
if (previous == null) {
- table[index] = entry.next;
+ data[index] = entry.next;
} else {
previous.next = entry.next;
}
@@ -460,52 +346,64 @@
}
-
+ //-----------------------------------------------------------------------
/**
- * Returns the size of this map.
- *
- * @return the size of this map
+ * Gets the size of the map.
+ *
+ * @return the size
*/
public int size() {
- purge();
- return size;
+ purgeBeforeRead();
+ return super.size();
}
-
/**
- * Returns <code>true</code> if this map is empty.
- *
- * @return <code>true</code> if this map is empty
+ * Checks whether the map is currently empty.
+ *
+ * @return true if the map is currently size zero
*/
public boolean isEmpty() {
- purge();
- return size == 0;
+ purgeBeforeRead();
+ return super.isEmpty();
}
-
/**
- * Returns <code>true</code> if this map contains the given key.
- *
- * @return true if the given key is in this map
+ * Checks whether the map contains the specified key.
+ *
+ * @param key the key to search for
+ * @return true if the map contains the key
*/
public boolean containsKey(Object key) {
- purge();
+ purgeBeforeRead();
Entry entry = getEntry(key);
if (entry == null) {
return false;
}
- return entry.getValue() != null;
+ return (entry.getValue() != null);
}
+ /**
+ * Checks whether the map contains the specified value.
+ *
+ * @param value the value to search for
+ * @return true if the map contains the value
+ */
+ public boolean containsValue(Object value) {
+ purgeBeforeRead();
+ if (value == null) {
+ return false;
+ }
+ return super.containsValue(value);
+ }
/**
- * Returns the value associated with the given key, if any.
- *
- * @return the value associated with the given key, or <code>null</code>
- * if the key maps to no value
+ * Gets the value mapped to the key specified.
+ *
+ * @param key the key
+ * @return the mapped value, null if no match
*/
public Object get(Object key) {
- purge();
+ purgeBeforeRead();
Entry entry = getEntry(key);
if (entry == null) {
return null;
@@ -515,15 +413,13 @@
/**
- * Associates the given key with the given value.<p>
- * Neither the key nor the value may be null.
- *
- * @param key the key of the mapping
- * @param value the value of the mapping
- * @return the last value associated with that key, or
- * null if no value was associated with the key
- * @throws NullPointerException if either the key or value
- * is null
+ * Puts a key-value mapping into this map.
+ * Neither the key nor the value may be null.
+ *
+ * @param key the key to add, must not be null
+ * @param value the value to add, must not be null
+ * @return the value previously mapped to this key, null if none
+ * @throws NullPointerException if either the key or value is null
*/
public Object put(Object key, Object value) {
if (key == null) {
@@ -533,215 +429,203 @@
throw new NullPointerException("null values not allowed");
}
- purge();
- if (size + 1 > threshold) {
- resize();
- }
-
- int hash = key.hashCode();
- int index = indexFor(hash);
- Entry entry = table[index];
- while (entry != null) {
- if ((hash == entry.hash) && key.equals(entry.getKey())) {
- Object result = entry.getValue();
- entry.setValue(value);
- return result;
- }
- entry = entry.next;
- }
- this.size++;
- modCount++;
- key = toReference(keyType, key, hash);
- value = toReference(valueType, value, hash);
- table[index] = new Entry(key, hash, value, table[index]);
- return null;
+ purgeBeforeWrite();
+ return super.put(key, value);
}
-
-
+
/**
- * Removes the key and its associated value from this map.
- *
- * @param key the key to remove
- * @return the value associated with that key, or null if
- * the key was not in the map
+ * Removes the specified mapping from this map.
+ *
+ * @param key the mapping to remove
+ * @return the value mapped to the removed key, null if key not in map
*/
public Object remove(Object key) {
if (key == null) {
return null;
}
- purge();
- int hash = key.hashCode();
- int index = indexFor(hash);
- Entry previous = null;
- Entry entry = table[index];
- while (entry != null) {
- if ((hash == entry.hash) && key.equals(entry.getKey())) {
- if (previous == null) {
- table[index] = entry.next;
- } else {
- previous.next = entry.next;
- }
- this.size--;
- modCount++;
- return entry.getValue();
- }
- previous = entry;
- entry = entry.next;
- }
- return null;
+ purgeBeforeWrite();
+ return super.remove(key);
}
-
/**
- * Clears this map.
+ * Clears this map.
*/
public void clear() {
- Arrays.fill(table, null);
- size = 0;
+ super.clear();
while (queue.poll() != null) {} // drain the queue
}
-
+ //-----------------------------------------------------------------------
/**
- * Returns a set view of this map's entries.
- *
- * @return a set view of this map's entries
+ * Compares two keys, in internal converted form, to see if they are equal.
+ * <p>
+ * This implementation converts the key from the entry to a real reference
+ * before comparison.
+ *
+ * @param key1 the first key to compare passed in from outside
+ * @param key2 the second key extracted from the entry via
<code>entry.key</code>
+ * @return true if equal
+ * @since Commons Collections 3.1
+ */
+ protected boolean isEqualKey(Object key1, Object key2) {
+ key2 = (keyType > HARD ? ((Reference) key2).get() : key2);
+ return (key1 == key2 || key1.equals(key2));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates a ReferenceEntry instead of a HashEntry.
+ *
+ * @param next the next entry in sequence
+ * @param hashCode the hash code to use
+ * @param key the key to store
+ * @param value the value to store
+ * @return the newly created entry
+ * @since Commons Collections 3.1
*/
- public Set entrySet() {
- if (entrySet != null) {
- return entrySet;
- }
- entrySet = new AbstractSet() {
- public int size() {
- return ReferenceMap.this.size();
- }
+ protected HashEntry createEntry(HashEntry next, int hashCode, Object key,
Object value) {
+ return new ReferenceEntry(this, next, hashCode, key, value);
+ }
- public void clear() {
- ReferenceMap.this.clear();
- }
+ /**
+ * Creates an entry set iterator.
+ *
+ * @return the entrySet iterator
+ * @since Commons Collections 3.1
+ */
+ protected Iterator createEntrySetIterator() {
+ return new ReferenceEntrySetIterator(this);
+ }
- public boolean contains(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj instanceof Map.Entry == false) {
- return false;
- }
- Map.Entry e = (Map.Entry) obj;
- Entry e2 = getEntry(e.getKey());
- return (e2 != null) && e.equals(e2);
- }
+ /**
+ * Creates an key set iterator.
+ *
+ * @return the keySet iterator
+ * @since Commons Collections 3.1
+ */
+ protected Iterator createKeySetIterator() {
+ return new ReferenceKeySetIterator(this);
+ }
- public boolean remove(Object obj) {
- boolean r = contains(obj);
- if (r) {
- Map.Entry e = (Map.Entry) obj;
- ReferenceMap.this.remove(e.getKey());
- }
- return r;
- }
+ /**
+ * Creates an values iterator.
+ *
+ * @return the values iterator
+ * @since Commons Collections 3.1
+ */
+ protected Iterator createValuesIterator() {
+ return new ReferenceValuesIterator(this);
+ }
- public Iterator iterator() {
- return new EntryIterator();
- }
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a set view of this map's entries.
+ * The <code>setValue()</code> method on the entries has no effect.
+ *
+ * @return a set view of this map's entries
+ */
+ public Set entrySet() {
+ if (entrySet == null) {
+ entrySet = new ReferenceEntrySet(this);
+ }
+ return entrySet;
+ }
+
+ /**
+ * EntrySet implementation.
+ */
+ static class ReferenceEntrySet extends EntrySet {
+
+ protected ReferenceEntrySet(AbstractHashedMap parent) {
+ super(parent);
+ }
- public Object[] toArray() {
- return toArray(new Object[0]);
- }
+ public Object[] toArray() {
+ return toArray(new Object[0]);
+ }
- public Object[] toArray(Object[] arr) {
- ArrayList list = new ArrayList();
- Iterator iterator = iterator();
- while (iterator.hasNext()) {
- Entry e = (Entry)iterator.next();
- list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
- }
- return list.toArray(arr);
+ public Object[] toArray(Object[] arr) {
+ // special implementation to handle disappearing entries
+ ArrayList list = new ArrayList();
+ Iterator iterator = iterator();
+ while (iterator.hasNext()) {
+ Entry e = (Entry) iterator.next();
+ list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
}
- };
- return entrySet;
+ return list.toArray(arr);
+ }
}
-
+ //-----------------------------------------------------------------------
/**
- * Returns a set view of this map's keys.
+ * Returns a set view of this map's keys.
*
- * @return a set view of this map's keys
+ * @return a set view of this map's keys
*/
public Set keySet() {
- if (keySet != null) return keySet;
- keySet = new AbstractSet() {
- public int size() {
- return ReferenceMap.this.size();
- }
-
- public Iterator iterator() {
- return new KeyIterator();
- }
-
- public boolean contains(Object obj) {
- return containsKey(obj);
- }
-
-
- public boolean remove(Object obj) {
- Object r = ReferenceMap.this.remove(obj);
- return r != null;
- }
-
- public void clear() {
- ReferenceMap.this.clear();
- }
+ if (keySet == null) {
+ keySet = new ReferenceKeySet(this);
+ }
+ return keySet;
+ }
+
+ /**
+ * KeySet implementation.
+ */
+ static class ReferenceKeySet extends KeySet {
+
+ protected ReferenceKeySet(AbstractHashedMap parent) {
+ super(parent);
+ }
- public Object[] toArray() {
- return toArray(new Object[0]);
- }
+ public Object[] toArray() {
+ return toArray(new Object[0]);
+ }
- public Object[] toArray(Object[] arr) {
- Collection c = new ArrayList(size());
- for (Iterator it = iterator(); it.hasNext(); ) {
- c.add(it.next());
- }
- return c.toArray(arr);
+ public Object[] toArray(Object[] arr) {
+ // special implementation to handle disappearing keys
+ List list = new ArrayList(parent.size());
+ for (Iterator it = iterator(); it.hasNext(); ) {
+ list.add(it.next());
}
- };
- return keySet;
+ return list.toArray(arr);
+ }
}
-
+ //-----------------------------------------------------------------------
/**
- * Returns a collection view of this map's values.
+ * Returns a collection view of this map's values.
*
- * @return a collection view of this map's values.
+ * @return a set view of this map's values
*/
public Collection values() {
- if (values != null) return values;
- values = new AbstractCollection() {
- public int size() {
- return ReferenceMap.this.size();
- }
-
- public void clear() {
- ReferenceMap.this.clear();
- }
-
- public Iterator iterator() {
- return new ValueIterator();
- }
+ if (values == null) {
+ values = new ReferenceValues(this);
+ }
+ return values;
+ }
+
+ /**
+ * Values implementation.
+ */
+ static class ReferenceValues extends Values {
+
+ protected ReferenceValues(AbstractHashedMap parent) {
+ super(parent);
+ }
- public Object[] toArray() {
- return toArray(new Object[0]);
- }
+ public Object[] toArray() {
+ return toArray(new Object[0]);
+ }
- public Object[] toArray(Object[] arr) {
- Collection c = new ArrayList(size());
- for (Iterator it = iterator(); it.hasNext(); ) {
- c.add(it.next());
- }
- return c.toArray(arr);
+ public Object[] toArray(Object[] arr) {
+ // special implementation to handle disappearing values
+ List list = new ArrayList(parent.size());
+ for (Iterator it = iterator(); it.hasNext(); ) {
+ list.add(it.next());
}
- };
- return values;
+ return list.toArray(arr);
+ }
}
//-----------------------------------------------------------------------
@@ -751,34 +635,40 @@
* If getKey() or getValue() returns null, it means
* the mapping is stale and should be removed.
*/
- private class Entry implements Map.Entry, KeyValue {
-
- Object key;
- Object value;
- int hash;
- Entry next;
-
- public Entry(Object key, int hash, Object value, Entry next) {
- this.key = key;
- this.hash = hash;
- this.value = value;
- this.next = next;
+ protected static class ReferenceEntry extends HashEntry {
+ /** The parent map */
+ protected final ReferenceMap parent;
+
+ /**
+ * Creates a new entry object for the ReferenceMap.
+ *
+ * @param parent the parent map
+ * @param next the next entry in the hash bucket
+ * @param hashCode the hash code of the key
+ * @param key the key
+ * @param value the value
+ */
+ public ReferenceEntry(ReferenceMap parent, HashEntry next, int hashCode,
Object key, Object value) {
+ super(next, hashCode, null, null);
+ this.parent = parent;
+ this.key = toReference(parent.keyType, key, hashCode);
+ this.value = toReference(parent.valueType, value, hashCode);
}
public Object getKey() {
- return (keyType > HARD) ? ((Reference)key).get() : key;
+ return (parent.keyType > HARD) ? ((Reference) key).get() : key;
}
public Object getValue() {
- return (valueType > HARD) ? ((Reference)value).get() : value;
+ return (parent.valueType > HARD) ? ((Reference) value).get() : value;
}
public Object setValue(Object obj) {
Object old = getValue();
- if (valueType > HARD) {
+ if (parent.valueType > HARD) {
((Reference)value).clear();
}
- value = toReference(valueType, obj, hash);
+ value = toReference(parent.valueType, obj, hashCode);
return old;
}
@@ -799,40 +689,59 @@
return key.equals(getKey()) && value.equals(getValue());
}
- public int hashCode() {
- Object v = getValue();
- return hash ^ ((v == null) ? 0 : v.hashCode());
- }
-
- public String toString() {
- return getKey() + "=" + getValue();
+ /**
+ * Constructs a reference of the given type to the given referent.
+ * The reference is registered with the queue for later purging.
+ *
+ * @param type HARD, SOFT or WEAK
+ * @param referent the object to refer to
+ * @param hash the hash code of the <i>key</i> of the mapping;
+ * this number might be different from referent.hashCode() if
+ * the referent represents a value and not a key
+ * @since Commons Collections 3.1
+ */
+ protected Object toReference(int type, Object referent, int hash) {
+ switch (type) {
+ case HARD: return referent;
+ case SOFT: return new SoftRef(hash, referent, parent.queue);
+ case WEAK: return new WeakRef(hash, referent, parent.queue);
+ default: throw new Error();
+ }
}
boolean purge(Reference ref) {
- boolean r = (keyType > HARD) && (key == ref);
- r = r || ((valueType > HARD) && (value == ref));
+ boolean r = (parent.keyType > HARD) && (key == ref);
+ r = r || ((parent.valueType > HARD) && (value == ref));
if (r) {
- if (keyType > HARD) {
+ if (parent.keyType > HARD) {
((Reference)key).clear();
}
- if (valueType > HARD) {
+ if (parent.valueType > HARD) {
((Reference)value).clear();
- } else if (purgeValues) {
+ } else if (parent.purgeValues) {
value = null;
}
}
return r;
}
+
+ ReferenceEntry next() {
+ return (ReferenceEntry) next;
+ }
}
+ //-----------------------------------------------------------------------
/**
* The EntrySet iterator.
*/
- private class EntryIterator implements Iterator {
+ static class ReferenceEntrySetIterator implements Iterator {
+ /** The parent map */
+ final ReferenceMap parent;
+
// These fields keep track of where we are in the table.
int index;
- Entry entry;
- Entry previous;
+ ReferenceEntry entry;
+ ReferenceEntry previous;
// These Object fields provide hard references to the
// current and next entry; this assures that if hasNext()
@@ -842,21 +751,23 @@
int expectedModCount;
- public EntryIterator() {
- index = (size() != 0 ? table.length : 0);
+ public ReferenceEntrySetIterator(ReferenceMap parent) {
+ super();
+ this.parent = parent;
+ index = (parent.size() != 0 ? parent.data.length : 0);
// have to do this here! size() invocation above
// may have altered the modCount.
- expectedModCount = modCount;
+ expectedModCount = parent.modCount;
}
public boolean hasNext() {
checkMod();
while (nextNull()) {
- Entry e = entry;
+ ReferenceEntry e = entry;
int i = index;
while ((e == null) && (i > 0)) {
i--;
- e = table[i];
+ e = (ReferenceEntry) parent.data[i];
}
entry = e;
index = i;
@@ -868,14 +779,14 @@
nextKey = e.getKey();
nextValue = e.getValue();
if (nextNull()) {
- entry = entry.next;
+ entry = entry.next();
}
}
return true;
}
private void checkMod() {
- if (modCount != expectedModCount) {
+ if (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
@@ -890,7 +801,7 @@
throw new NoSuchElementException();
}
previous = entry;
- entry = entry.next;
+ entry = entry.next();
currentKey = nextKey;
currentValue = nextValue;
nextKey = null;
@@ -907,29 +818,39 @@
if (previous == null) {
throw new IllegalStateException();
}
- ReferenceMap.this.remove(currentKey);
+ parent.remove(currentKey);
previous = null;
currentKey = null;
currentValue = null;
- expectedModCount = modCount;
+ expectedModCount = parent.modCount;
}
}
/**
- * The values iterator.
+ * The keySet iterator.
*/
- private class ValueIterator extends EntryIterator {
+ static class ReferenceKeySetIterator extends ReferenceEntrySetIterator {
+
+ ReferenceKeySetIterator(ReferenceMap parent) {
+ super(parent);
+ }
+
public Object next() {
- return nextEntry().getValue();
+ return nextEntry().getKey();
}
}
/**
- * The keySet iterator.
+ * The values iterator.
*/
- private class KeyIterator extends EntryIterator {
+ static class ReferenceValuesIterator extends ReferenceEntrySetIterator {
+
+ ReferenceValuesIterator(ReferenceMap parent) {
+ super(parent);
+ }
+
public Object next() {
- return nextEntry().getKey();
+ return nextEntry().getValue();
}
}
@@ -941,7 +862,7 @@
/**
* A soft reference holder.
*/
- private static class SoftRef extends SoftReference {
+ static class SoftRef extends SoftReference {
private int hash;
public SoftRef(int hash, Object r, ReferenceQueue q) {
@@ -957,7 +878,7 @@
/**
* A weak reference holder.
*/
- private static class WeakRef extends WeakReference {
+ static class WeakRef extends WeakReference {
private int hash;
public WeakRef(int hash, Object r, ReferenceQueue q) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]