commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From scolebou...@apache.org
Subject cvs commit: jakarta-commons/collections/src/java/org/apache/commons/collections/map ReferenceMap.java
Date Thu, 01 Apr 2004 00:07:48 GMT
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
  - * {@link 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 {@link 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 {@link java.util.HashMap}.  In particular, you 
  - * can specify a load factor and capacity to suit your
  - * needs.  All optional {@link Map} operations are 
  - * supported.
  + * This {@link 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 {@link 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 {@link java.util.Collections#synchronizedMap} to 
  + * As usual, this implementation is not synchronized.
  + * You can use {@link 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 {@link #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 {@link #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: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message