mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			991 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			991 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* IdentityHashMap.java -- a class providing a hashtable data structure,
 | |
|    mapping Object --> Object, which uses object identity for hashing.
 | |
|    Copyright (C) 2001, 2002, 2004, 2005  Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| package java.util;
 | |
| 
 | |
| import java.io.IOException;
 | |
| import java.io.ObjectInputStream;
 | |
| import java.io.ObjectOutputStream;
 | |
| import java.io.Serializable;
 | |
| 
 | |
| /**
 | |
|  * This class provides a hashtable-backed implementation of the
 | |
|  * Map interface, but uses object identity to do its hashing.  In fact,
 | |
|  * it uses object identity for comparing values, as well. It uses a
 | |
|  * linear-probe hash table, which may have faster performance
 | |
|  * than the chaining employed by HashMap.
 | |
|  * <p>
 | |
|  *
 | |
|  * <em>WARNING: This is not a general purpose map. Because it uses
 | |
|  * System.identityHashCode and ==, instead of hashCode and equals, for
 | |
|  * comparison, it violated Map's general contract, and may cause
 | |
|  * undefined behavior when compared to other maps which are not
 | |
|  * IdentityHashMaps.  This is designed only for the rare cases when
 | |
|  * identity semantics are needed.</em> An example use is
 | |
|  * topology-preserving graph transformations, such as deep cloning,
 | |
|  * or as proxy object mapping such as in debugging.
 | |
|  * <p>
 | |
|  *
 | |
|  * This map permits <code>null</code> keys and values, and does not
 | |
|  * guarantee that elements will stay in the same order over time. The
 | |
|  * basic operations (<code>get</code> and <code>put</code>) take
 | |
|  * constant time, provided System.identityHashCode is decent. You can
 | |
|  * tune the behavior by specifying the expected maximum size. As more
 | |
|  * elements are added, the map may need to allocate a larger table,
 | |
|  * which can be expensive.
 | |
|  * <p>
 | |
|  *
 | |
|  * This implementation is unsynchronized.  If you want multi-thread
 | |
|  * access to be consistent, you must synchronize it, perhaps by using
 | |
|  * <code>Collections.synchronizedMap(new IdentityHashMap(...));</code>.
 | |
|  * The iterators are <i>fail-fast</i>, meaning that a structural modification
 | |
|  * made to the map outside of an iterator's remove method cause the
 | |
|  * iterator, and in the case of the entrySet, the Map.Entry, to
 | |
|  * fail with a {@link ConcurrentModificationException}.
 | |
|  *
 | |
|  * @author Tom Tromey (tromey@redhat.com)
 | |
|  * @author Eric Blake (ebb9@email.byu.edu)
 | |
|  * @see System#identityHashCode(Object)
 | |
|  * @see Collection
 | |
|  * @see Map
 | |
|  * @see HashMap
 | |
|  * @see TreeMap
 | |
|  * @see LinkedHashMap
 | |
|  * @see WeakHashMap
 | |
|  * @since 1.4
 | |
|  * @status updated to 1.4
 | |
|  */
 | |
| public class IdentityHashMap<K,V> extends AbstractMap<K,V>
 | |
|   implements Map<K,V>, Serializable, Cloneable
 | |
| {
 | |
|   /** The default capacity. */
 | |
|   private static final int DEFAULT_CAPACITY = 21;
 | |
| 
 | |
|   /**
 | |
|    * This object is used to mark a slot whose key or value is 'null'.
 | |
|    * This is more efficient than using a special value to mark an empty
 | |
|    * slot, because null entries are rare, empty slots are common, and
 | |
|    * the JVM will clear new arrays for us.
 | |
|    * Package visible for use by nested classes.
 | |
|    */
 | |
|   static final Object nullslot = new Object();
 | |
| 
 | |
|   /**
 | |
|    * Compatible with JDK 1.4.
 | |
|    */
 | |
|   private static final long serialVersionUID = 8188218128353913216L;
 | |
| 
 | |
|   /**
 | |
|    * The number of mappings in the table. Package visible for use by nested
 | |
|    * classes.
 | |
|    * @serial
 | |
|    */
 | |
|   int size;
 | |
| 
 | |
|   /**
 | |
|    * The table itself. Package visible for use by nested classes.
 | |
|    */
 | |
|   transient Object[] table;
 | |
| 
 | |
|   /**
 | |
|    * The number of structural modifications made so far. Package visible for
 | |
|    * use by nested classes.
 | |
|    */
 | |
|   transient int modCount;
 | |
| 
 | |
|   /**
 | |
|    * The cache for {@link #entrySet()}.
 | |
|    */
 | |
|   private transient Set<Map.Entry<K,V>> entries;
 | |
| 
 | |
|   /**
 | |
|    * The threshold for rehashing, which is 75% of (table.length / 2).
 | |
|    */
 | |
|   private transient int threshold;
 | |
| 
 | |
|   /**
 | |
|    * Create a new IdentityHashMap with the default capacity (21 entries).
 | |
|    */
 | |
|   public IdentityHashMap()
 | |
|   {
 | |
|     this(DEFAULT_CAPACITY);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Create a new IdentityHashMap with the indicated number of
 | |
|    * entries.  If the number of elements added to this hash map
 | |
|    * exceeds this maximum, the map will grow itself; however, that
 | |
|    * incurs a performance penalty.
 | |
|    *
 | |
|    * @param max initial size
 | |
|    * @throws IllegalArgumentException if max is negative
 | |
|    */
 | |
|   public IdentityHashMap(int max)
 | |
|   {
 | |
|     if (max < 0)
 | |
|       throw new IllegalArgumentException();
 | |
|     // Need at least two slots, or hash() will break.
 | |
|     if (max < 2)
 | |
|       max = 2;
 | |
|     table = new Object[max << 1];
 | |
|     threshold = (max >> 2) * 3;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Create a new IdentityHashMap whose contents are taken from the
 | |
|    * given Map.
 | |
|    *
 | |
|    * @param m The map whose elements are to be put in this map
 | |
|    * @throws NullPointerException if m is null
 | |
|    */
 | |
|   public IdentityHashMap(Map<? extends K, ? extends V> m)
 | |
|   {
 | |
|     this(Math.max(m.size() << 1, DEFAULT_CAPACITY));
 | |
|     putAll(m);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remove all mappings from this map.
 | |
|    */
 | |
|   public void clear()
 | |
|   {
 | |
|     if (size != 0)
 | |
|       {
 | |
|         modCount++;
 | |
|         Arrays.fill(table, null);
 | |
|         size = 0;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Creates a shallow copy where keys and values are not cloned.
 | |
|    */
 | |
|   public Object clone()
 | |
|   {
 | |
|     try
 | |
|       {
 | |
|         IdentityHashMap copy = (IdentityHashMap) super.clone();
 | |
|         copy.table = (Object[]) table.clone();
 | |
|         copy.entries = null; // invalidate the cache
 | |
|         return copy;
 | |
|       }
 | |
|     catch (CloneNotSupportedException e)
 | |
|       {
 | |
|         // Can't happen.
 | |
|         return null;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Tests whether the specified key is in this map.  Unlike normal Maps,
 | |
|    * this test uses <code>entry == key</code> instead of
 | |
|    * <code>entry == null ? key == null : entry.equals(key)</code>.
 | |
|    *
 | |
|    * @param key the key to look for
 | |
|    * @return true if the key is contained in the map
 | |
|    * @see #containsValue(Object)
 | |
|    * @see #get(Object)
 | |
|    */
 | |
|   public boolean containsKey(Object key)
 | |
|   {
 | |
|     key = xform(key);
 | |
|     return key == table[hash(key)];
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns true if this HashMap contains the value.  Unlike normal maps,
 | |
|    * this test uses <code>entry == value</code> instead of
 | |
|    * <code>entry == null ? value == null : entry.equals(value)</code>.
 | |
|    *
 | |
|    * @param value the value to search for in this HashMap
 | |
|    * @return true if at least one key maps to the value
 | |
|    * @see #containsKey(Object)
 | |
|    */
 | |
|   public boolean containsValue(Object value)
 | |
|   {
 | |
|     value = xform(value);
 | |
|     for (int i = table.length - 1; i > 0; i -= 2)
 | |
|       if (table[i] == value)
 | |
|         return true;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a "set view" of this Map's entries. The set is backed by
 | |
|    * the Map, so changes in one show up in the other.  The set supports
 | |
|    * element removal, but not element addition.
 | |
|    * <p>
 | |
|    *
 | |
|    * <em>The semantics of this set, and of its contained entries, are
 | |
|    * different from the contract of Set and Map.Entry in order to make
 | |
|    * IdentityHashMap work.  This means that while you can compare these
 | |
|    * objects between IdentityHashMaps, comparing them with regular sets
 | |
|    * or entries is likely to have undefined behavior.</em>  The entries
 | |
|    * in this set are reference-based, rather than the normal object
 | |
|    * equality.  Therefore, <code>e1.equals(e2)</code> returns
 | |
|    * <code>e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue()</code>,
 | |
|    * and <code>e.hashCode()</code> returns
 | |
|    * <code>System.identityHashCode(e.getKey()) ^
 | |
|    *       System.identityHashCode(e.getValue())</code>.
 | |
|    * <p>
 | |
|    *
 | |
|    * Note that the iterators for all three views, from keySet(), entrySet(),
 | |
|    * and values(), traverse the Map in the same sequence.
 | |
|    *
 | |
|    * @return a set view of the entries
 | |
|    * @see #keySet()
 | |
|    * @see #values()
 | |
|    * @see Map.Entry
 | |
|    */
 | |
|   public Set<Map.Entry<K,V>> entrySet()
 | |
|   {
 | |
|     if (entries == null)
 | |
|       entries = new AbstractSet<Map.Entry<K,V>>()
 | |
|       {
 | |
|         public int size()
 | |
|         {
 | |
|           return size;
 | |
|         }
 | |
| 
 | |
|         public Iterator<Map.Entry<K,V>> iterator()
 | |
|         {
 | |
|           return new IdentityIterator<Map.Entry<K,V>>(ENTRIES);
 | |
|         }
 | |
| 
 | |
|         public void clear()
 | |
|         {
 | |
|           IdentityHashMap.this.clear();
 | |
|         }
 | |
| 
 | |
|         public boolean contains(Object o)
 | |
|         {
 | |
|           if (! (o instanceof Map.Entry))
 | |
|             return false;
 | |
|           Map.Entry m = (Map.Entry) o;
 | |
|           Object value = xform(m.getValue());
 | |
|           Object key = xform(m.getKey());
 | |
|           return value == table[hash(key) + 1];
 | |
|         }
 | |
| 
 | |
|         public int hashCode()
 | |
|         {
 | |
|           return IdentityHashMap.this.hashCode();
 | |
|         }
 | |
| 
 | |
|         public boolean remove(Object o)
 | |
|         {
 | |
|           if (! (o instanceof Map.Entry))
 | |
|             return false;
 | |
|           Object key = xform(((Map.Entry) o).getKey());
 | |
|           int h = hash(key);
 | |
|           if (table[h] == key)
 | |
|             {
 | |
|               size--;
 | |
|               modCount++;
 | |
|               IdentityHashMap.this.removeAtIndex(h);
 | |
|               return true;
 | |
|             }
 | |
|           return false;
 | |
|         }
 | |
|       };
 | |
|     return entries;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Compares two maps for equality. This returns true only if both maps
 | |
|    * have the same reference-identity comparisons. While this returns
 | |
|    * <code>this.entrySet().equals(m.entrySet())</code> as specified by Map,
 | |
|    * this will not work with normal maps, since the entry set compares
 | |
|    * with == instead of .equals.
 | |
|    *
 | |
|    * @param o the object to compare to
 | |
|    * @return true if it is equal
 | |
|    */
 | |
|   public boolean equals(Object o)
 | |
|   {
 | |
|     // Why did Sun specify this one? The superclass does the right thing.
 | |
|     return super.equals(o);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Return the value in this Map associated with the supplied key, or
 | |
|    * <code>null</code> if the key maps to nothing.
 | |
|    *
 | |
|    * <p>NOTE: Since the value could also be null, you must use
 | |
|    * containsKey to see if this key actually maps to something.
 | |
|    * Unlike normal maps, this tests for the key with <code>entry ==
 | |
|    * key</code> instead of <code>entry == null ? key == null :
 | |
|    * entry.equals(key)</code>.
 | |
|    *
 | |
|    * @param key the key for which to fetch an associated value
 | |
|    * @return what the key maps to, if present
 | |
|    * @see #put(Object, Object)
 | |
|    * @see #containsKey(Object)
 | |
|    */
 | |
|   public V get(Object key)
 | |
|   {
 | |
|     key = xform(key);
 | |
|     int h = hash(key);
 | |
|     return (V) (table[h] == key ? unxform(table[h + 1]) : null);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the hashcode of this map. This guarantees that two
 | |
|    * IdentityHashMaps that compare with equals() will have the same hash code,
 | |
|    * but may break with comparison to normal maps since it uses
 | |
|    * System.identityHashCode() instead of hashCode().
 | |
|    *
 | |
|    * @return the hash code
 | |
|    */
 | |
|   public int hashCode()
 | |
|   {
 | |
|     int hash = 0;
 | |
|     for (int i = table.length - 2; i >= 0; i -= 2)
 | |
|       {
 | |
|         Object key = table[i];
 | |
|         if (key == null)
 | |
|           continue;
 | |
|         // FIXME: this is a lame computation.
 | |
|         hash += (System.identityHashCode(unxform(key))
 | |
|                  ^ System.identityHashCode(unxform(table[i + 1])));
 | |
|       }
 | |
|     return hash;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns true if there are no key-value mappings currently in this Map
 | |
|    * @return <code>size() == 0</code>
 | |
|    */
 | |
|   public boolean isEmpty()
 | |
|   {
 | |
|     return size == 0;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a "set view" of this Map's keys. The set is backed by the
 | |
|    * Map, so changes in one show up in the other.  The set supports
 | |
|    * element removal, but not element addition.
 | |
|    * <p>
 | |
|    *
 | |
|    * <em>The semantics of this set are different from the contract of Set
 | |
|    * in order to make IdentityHashMap work.  This means that while you can
 | |
|    * compare these objects between IdentityHashMaps, comparing them with
 | |
|    * regular sets is likely to have undefined behavior.</em>  The hashCode
 | |
|    * of the set is the sum of the identity hash codes, instead of the
 | |
|    * regular hashCodes, and equality is determined by reference instead
 | |
|    * of by the equals method.
 | |
|    * <p>
 | |
|    *
 | |
|    * @return a set view of the keys
 | |
|    * @see #values()
 | |
|    * @see #entrySet()
 | |
|    */
 | |
|   public Set<K> keySet()
 | |
|   {
 | |
|     if (keys == null)
 | |
|       keys = new AbstractSet<K>()
 | |
|       {
 | |
|         public int size()
 | |
|         {
 | |
|           return size;
 | |
|         }
 | |
| 
 | |
|         public Iterator<K> iterator()
 | |
|         {
 | |
|           return new IdentityIterator<K>(KEYS);
 | |
|         }
 | |
| 
 | |
|         public void clear()
 | |
|         {
 | |
|           IdentityHashMap.this.clear();
 | |
|         }
 | |
| 
 | |
|         public boolean contains(Object o)
 | |
|         {
 | |
|           return containsKey(o);
 | |
|         }
 | |
| 
 | |
|         public int hashCode()
 | |
|         {
 | |
|           int hash = 0;
 | |
|           for (int i = table.length - 2; i >= 0; i -= 2)
 | |
|             {
 | |
|               Object key = table[i];
 | |
|               if (key == null)
 | |
|                 continue;
 | |
|               hash += System.identityHashCode(unxform(key));
 | |
|             }
 | |
|           return hash;
 | |
|         }
 | |
| 
 | |
|         public boolean remove(Object o)
 | |
|         {
 | |
|           o = xform(o);
 | |
|           int h = hash(o);
 | |
|           if (table[h] == o)
 | |
|             {
 | |
|               size--;
 | |
|               modCount++;
 | |
|               removeAtIndex(h);
 | |
|               return true;
 | |
|             }
 | |
|           return false;
 | |
|         }
 | |
|       };
 | |
|     return keys;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Puts the supplied value into the Map, mapped by the supplied key.
 | |
|    * The value may be retrieved by any object which <code>equals()</code>
 | |
|    * this key. NOTE: Since the prior value could also be null, you must
 | |
|    * first use containsKey if you want to see if you are replacing the
 | |
|    * key's mapping.  Unlike normal maps, this tests for the key
 | |
|    * with <code>entry == key</code> instead of
 | |
|    * <code>entry == null ? key == null : entry.equals(key)</code>.
 | |
|    *
 | |
|    * @param key the key used to locate the value
 | |
|    * @param value the value to be stored in the HashMap
 | |
|    * @return the prior mapping of the key, or null if there was none
 | |
|    * @see #get(Object)
 | |
|    */
 | |
|   public V put(K key, V value)
 | |
|   {
 | |
|     key = (K) xform(key);
 | |
|     value = (V) xform(value);
 | |
| 
 | |
|     // We don't want to rehash if we're overwriting an existing slot.
 | |
|     int h = hash(key);
 | |
|     if (table[h] == key)
 | |
|       {
 | |
|         V r = (V) unxform(table[h + 1]);
 | |
|         table[h + 1] = value;
 | |
|         return r;
 | |
|       }
 | |
| 
 | |
|     // Rehash if the load factor is too high.
 | |
|     if (size > threshold)
 | |
|       {
 | |
|         Object[] old = table;
 | |
|         // This isn't necessarily prime, but it is an odd number of key/value
 | |
|         // slots, which has a higher probability of fewer collisions.
 | |
|         table = new Object[(old.length * 2) + 2];
 | |
|         size = 0;
 | |
|         threshold = (table.length >>> 3) * 3;
 | |
| 
 | |
|         for (int i = old.length - 2; i >= 0; i -= 2)
 | |
|           {
 | |
|             K oldkey = (K) old[i];
 | |
|             if (oldkey != null)
 | |
|               {
 | |
|                 h = hash(oldkey);
 | |
|                 table[h] = oldkey;
 | |
|                 table[h + 1] = old[i + 1];
 | |
|                 ++size;
 | |
|                 // No need to update modCount here, we'll do it
 | |
|                 // just after the loop.
 | |
|               }
 | |
|           }
 | |
| 
 | |
|         // Now that we've resize, recompute the hash value.
 | |
|         h = hash(key);
 | |
|       }
 | |
| 
 | |
|     // At this point, we add a new mapping.
 | |
|     modCount++;
 | |
|     size++;
 | |
|     table[h] = key;
 | |
|     table[h + 1] = value;
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Copies all of the mappings from the specified map to this. If a key
 | |
|    * is already in this map, its value is replaced.
 | |
|    *
 | |
|    * @param m the map to copy
 | |
|    * @throws NullPointerException if m is null
 | |
|    */
 | |
|   public void putAll(Map<? extends K, ? extends V> m)
 | |
|   {
 | |
|     // Why did Sun specify this one? The superclass does the right thing.
 | |
|     super.putAll(m);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remove the element at index and update the table to compensate.
 | |
|    * This is package-private for use by inner classes.
 | |
|    * @param i index of the removed element
 | |
|    */
 | |
|   final void removeAtIndex(int i)
 | |
|   {
 | |
|     // This is Algorithm R from Knuth, section 6.4.
 | |
|     // Variable names are taken directly from the text.
 | |
|     while (true)
 | |
|       {
 | |
|         table[i] = null;
 | |
|         table[i + 1] = null;
 | |
|         int j = i;
 | |
|         int r;
 | |
|         do
 | |
|           {
 | |
|             i -= 2;
 | |
|             if (i < 0)
 | |
|               i = table.length - 2;
 | |
|             Object key = table[i];
 | |
|             if (key == null)
 | |
|               return;
 | |
|             r = Math.abs(System.identityHashCode(key)
 | |
|                          % (table.length >> 1)) << 1;
 | |
|           }
 | |
|         while ((i <= r && r < j)
 | |
|             || (r < j && j < i)
 | |
|             || (j < i && i <= r));
 | |
|         table[j] = table[i];
 | |
|         table[j + 1] = table[i + 1];
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Removes from the HashMap and returns the value which is mapped by
 | |
|    * the supplied key. If the key maps to nothing, then the HashMap
 | |
|    * remains unchanged, and <code>null</code> is returned.
 | |
|    *
 | |
|    * NOTE: Since the value could also be null, you must use
 | |
|    * containsKey to see if you are actually removing a mapping.
 | |
|    * Unlike normal maps, this tests for the key with <code>entry ==
 | |
|    * key</code> instead of <code>entry == null ? key == null :
 | |
|    * entry.equals(key)</code>.
 | |
|    *
 | |
|    * @param key the key used to locate the value to remove
 | |
|    * @return whatever the key mapped to, if present
 | |
|    */
 | |
|   public V remove(Object key)
 | |
|   {
 | |
|     key = xform(key);
 | |
|     int h = hash(key);
 | |
|     if (table[h] == key)
 | |
|       {
 | |
|         modCount++;
 | |
|         size--;
 | |
|         Object r = unxform(table[h + 1]);
 | |
|         removeAtIndex(h);
 | |
|         return (V) r;
 | |
|       }
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the number of kay-value mappings currently in this Map
 | |
|    * @return the size
 | |
|    */
 | |
|   public int size()
 | |
|   {
 | |
|     return size;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a "collection view" (or "bag view") of this Map's values.
 | |
|    * The collection is backed by the Map, so changes in one show up
 | |
|    * in the other.  The collection supports element removal, but not element
 | |
|    * addition.
 | |
|    * <p>
 | |
|    *
 | |
|    * <em>The semantics of this set are different from the contract of
 | |
|    * Collection in order to make IdentityHashMap work.  This means that
 | |
|    * while you can compare these objects between IdentityHashMaps, comparing
 | |
|    * them with regular sets is likely to have undefined behavior.</em>
 | |
|    * Likewise, contains and remove go by == instead of equals().
 | |
|    * <p>
 | |
|    *
 | |
|    * @return a bag view of the values
 | |
|    * @see #keySet()
 | |
|    * @see #entrySet()
 | |
|    */
 | |
|   public Collection<V> values()
 | |
|   {
 | |
|     if (values == null)
 | |
|       values = new AbstractCollection<V>()
 | |
|       {
 | |
|         public int size()
 | |
|         {
 | |
|           return size;
 | |
|         }
 | |
| 
 | |
|         public Iterator<V> iterator()
 | |
|         {
 | |
|           return new IdentityIterator<V>(VALUES);
 | |
|         }
 | |
| 
 | |
|         public void clear()
 | |
|         {
 | |
|           IdentityHashMap.this.clear();
 | |
|         }
 | |
| 
 | |
|         public boolean remove(Object o)
 | |
|         {
 | |
|           o = xform(o);
 | |
|           // This approach may look strange, but it is ok.
 | |
|           for (int i = table.length - 1; i > 0; i -= 2)
 | |
|             if (table[i] == o)
 | |
|               {
 | |
|                 modCount++;
 | |
|                 size--;
 | |
|                 IdentityHashMap.this.removeAtIndex(i - 1);
 | |
|                 return true;
 | |
|               }
 | |
|           return false;
 | |
|         }
 | |
|       };
 | |
|     return values;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Transform a reference from its external form to its internal form.
 | |
|    * This is package-private for use by inner classes.
 | |
|    */
 | |
|   final Object xform(Object o)
 | |
|   {
 | |
|     if (o == null)
 | |
|       o = nullslot;
 | |
|     return o;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Transform a reference from its internal form to its external form.
 | |
|    * This is package-private for use by inner classes.
 | |
|    */
 | |
|   final Object unxform(Object o)
 | |
|   {
 | |
|     if (o == nullslot)
 | |
|       o = null;
 | |
|     return o;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper method which computes the hash code, then traverses the table
 | |
|    * until it finds the key, or the spot where the key would go.  the key
 | |
|    * must already be in its internal form.
 | |
|    *
 | |
|    * @param key the key to check
 | |
|    * @return the index where the key belongs
 | |
|    * @see #IdentityHashMap(int)
 | |
|    * @see #put(Object, Object)
 | |
|    */
 | |
|   // Package visible for use by nested classes.
 | |
|   final int hash(Object key)
 | |
|   {
 | |
|     int h = Math.abs(System.identityHashCode(key) % (table.length >> 1)) << 1;
 | |
| 
 | |
|     while (true)
 | |
|       {
 | |
|         // By requiring at least 2 key/value slots, and rehashing at 75%
 | |
|         // capacity, we guarantee that there will always be either an empty
 | |
|         // slot somewhere in the table.
 | |
|         if (table[h] == key || table[h] == null)
 | |
|           return h;
 | |
|         // We use linear probing as it is friendlier to the cache and
 | |
|         // it lets us efficiently remove entries.
 | |
|         h -= 2;
 | |
|         if (h < 0)
 | |
|           h = table.length - 2;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This class allows parameterized iteration over IdentityHashMaps.  Based
 | |
|    * on its construction, it returns the key or value of a mapping, or
 | |
|    * creates the appropriate Map.Entry object with the correct fail-fast
 | |
|    * semantics and identity comparisons.
 | |
|    *
 | |
|    * @author Tom Tromey (tromey@redhat.com)
 | |
|    * @author Eric Blake (ebb9@email.byu.edu)
 | |
|    */
 | |
|   private class IdentityIterator<I> implements Iterator<I>
 | |
|   {
 | |
|     /**
 | |
|      * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
 | |
|      * or {@link #ENTRIES}.
 | |
|      */
 | |
|     final int type;
 | |
|     /** The number of modifications to the backing Map that we know about. */
 | |
|     int knownMod = modCount;
 | |
|     /** The number of elements remaining to be returned by next(). */
 | |
|     int count = size;
 | |
|     /** Location in the table. */
 | |
|     int loc = table.length;
 | |
| 
 | |
|     /**
 | |
|      * Construct a new Iterator with the supplied type.
 | |
|      * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
 | |
|      */
 | |
|     IdentityIterator(int type)
 | |
|     {
 | |
|       this.type = type;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns true if the Iterator has more elements.
 | |
|      * @return true if there are more elements
 | |
|      */
 | |
|     public boolean hasNext()
 | |
|     {
 | |
|       return count > 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the next element in the Iterator's sequential view.
 | |
|      * @return the next element
 | |
|      * @throws ConcurrentModificationException if the Map was modified
 | |
|      * @throws NoSuchElementException if there is none
 | |
|      */
 | |
|     public I next()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       if (count == 0)
 | |
|         throw new NoSuchElementException();
 | |
|       count--;
 | |
| 
 | |
|       Object key;
 | |
|       do
 | |
|         {
 | |
|           loc -= 2;
 | |
|           key = table[loc];
 | |
|         }
 | |
|       while (key == null);
 | |
| 
 | |
|       return (I) (type == KEYS ? unxform(key)
 | |
|                   : (type == VALUES ? unxform(table[loc + 1])
 | |
|                      : new IdentityEntry(loc)));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Removes from the backing Map the last element which was fetched
 | |
|      * with the <code>next()</code> method.
 | |
|      *
 | |
|      * @throws ConcurrentModificationException if the Map was modified
 | |
|      * @throws IllegalStateException if called when there is no last element
 | |
|      */
 | |
|     public void remove()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       if (loc == table.length)
 | |
|         throw new IllegalStateException();
 | |
|       modCount++;
 | |
|       size--;
 | |
|       removeAtIndex(loc);
 | |
|       knownMod++;
 | |
|     }
 | |
|   } // class IdentityIterator
 | |
| 
 | |
|   /**
 | |
|    * This class provides Map.Entry objects for IdentityHashMaps.  The entry
 | |
|    * is fail-fast, and will throw a ConcurrentModificationException if
 | |
|    * the underlying map is modified, or if remove is called on the iterator
 | |
|    * that generated this object.  It is identity based, so it violates
 | |
|    * the general contract of Map.Entry, and is probably unsuitable for
 | |
|    * comparison to normal maps; but it works among other IdentityHashMaps.
 | |
|    *
 | |
|    * @author Eric Blake (ebb9@email.byu.edu)
 | |
|    */
 | |
|   private final class IdentityEntry<EK,EV> implements Map.Entry<EK,EV>
 | |
|   {
 | |
|     /** The location of this entry. */
 | |
|     final int loc;
 | |
|     /** The number of modifications to the backing Map that we know about. */
 | |
|     final int knownMod = modCount;
 | |
| 
 | |
|     /**
 | |
|      * Constructs the Entry.
 | |
|      *
 | |
|      * @param loc the location of this entry in table
 | |
|      */
 | |
|     IdentityEntry(int loc)
 | |
|     {
 | |
|       this.loc = loc;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Compares the specified object with this entry, using identity
 | |
|      * semantics. Note that this can lead to undefined results with
 | |
|      * Entry objects created by normal maps.
 | |
|      *
 | |
|      * @param o the object to compare
 | |
|      * @return true if it is equal
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public boolean equals(Object o)
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       if (! (o instanceof Map.Entry))
 | |
|         return false;
 | |
|       Map.Entry e = (Map.Entry) o;
 | |
|       return table[loc] == xform(e.getKey())
 | |
|              && table[loc + 1] == xform(e.getValue());
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the key of this entry.
 | |
|      *
 | |
|      * @return the key
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public EK getKey()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       return (EK) unxform(table[loc]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the value of this entry.
 | |
|      *
 | |
|      * @return the value
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public EV getValue()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       return (EV) unxform(table[loc + 1]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the hashcode of the entry, using identity semantics.
 | |
|      * Note that this can lead to undefined results with Entry objects
 | |
|      * created by normal maps.
 | |
|      *
 | |
|      * @return the hash code
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public int hashCode()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       return (System.identityHashCode(unxform(table[loc]))
 | |
|               ^ System.identityHashCode(unxform(table[loc + 1])));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Replaces the value of this mapping, and returns the old value.
 | |
|      *
 | |
|      * @param value the new value
 | |
|      * @return the old value
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public EV setValue(EV value)
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       EV r = (EV) unxform(table[loc + 1]);
 | |
|       table[loc + 1] = xform(value);
 | |
|       return r;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * This provides a string representation of the entry. It is of the form
 | |
|      * "key=value", where string concatenation is used on key and value.
 | |
|      *
 | |
|      * @return the string representation
 | |
|      * @throws ConcurrentModificationException if the entry was invalidated
 | |
|      *         by modifying the Map or calling Iterator.remove()
 | |
|      */
 | |
|     public String toString()
 | |
|     {
 | |
|       if (knownMod != modCount)
 | |
|         throw new ConcurrentModificationException();
 | |
|       return unxform(table[loc]) + "=" + unxform(table[loc + 1]);
 | |
|     }
 | |
|   } // class IdentityEntry
 | |
| 
 | |
|   /**
 | |
|    * Reads the object from a serial stream.
 | |
|    *
 | |
|    * @param s the stream to read from
 | |
|    * @throws ClassNotFoundException if the underlying stream fails
 | |
|    * @throws IOException if the underlying stream fails
 | |
|    * @serialData expects the size (int), followed by that many key (Object)
 | |
|    *             and value (Object) pairs, with the pairs in no particular
 | |
|    *             order
 | |
|    */
 | |
|   private void readObject(ObjectInputStream s)
 | |
|     throws IOException, ClassNotFoundException
 | |
|   {
 | |
|     s.defaultReadObject();
 | |
| 
 | |
|     int num = s.readInt();
 | |
|     table = new Object[Math.max(num << 1, DEFAULT_CAPACITY) << 1];
 | |
|     // Read key/value pairs.
 | |
|     while (--num >= 0)
 | |
|       put((K) s.readObject(), (V) s.readObject());
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Writes the object to a serial stream.
 | |
|    *
 | |
|    * @param s the stream to write to
 | |
|    * @throws IOException if the underlying stream fails
 | |
|    * @serialData outputs the size (int), followed by that many key (Object)
 | |
|    *             and value (Object) pairs, with the pairs in no particular
 | |
|    *             order
 | |
|    */
 | |
|   private void writeObject(ObjectOutputStream s)
 | |
|     throws IOException
 | |
|   {
 | |
|     s.defaultWriteObject();
 | |
|     s.writeInt(size);
 | |
|     for (int i = table.length - 2; i >= 0; i -= 2)
 | |
|       {
 | |
|         Object key = table[i];
 | |
|         if (key != null)
 | |
|           {
 | |
|             s.writeObject(unxform(key));
 | |
|             s.writeObject(unxform(table[i + 1]));
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| }
 |