X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=libjava%2Fjava%2Futil%2FIdentityHashMap.java;fp=libjava%2Fjava%2Futil%2FIdentityHashMap.java;h=0000000000000000000000000000000000000000;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=4609f015f8d45df6e9c6121a18a5eb94024c6d6c;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/libjava/java/util/IdentityHashMap.java b/libjava/java/util/IdentityHashMap.java deleted file mode 100644 index 4609f015..00000000 --- a/libjava/java/util/IdentityHashMap.java +++ /dev/null @@ -1,927 +0,0 @@ -/* IdentityHashMap.java -- a class providing a hashtable data structure, - mapping Object --> Object, which uses object identity for hashing. - Copyright (C) 2001, 2002 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., 59 Temple Place, Suite 330, Boston, MA -02111-1307 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.*; - -/** - * 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. - *

- * - * 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. An example use is - * topology-preserving graph transformations, such as deep cloning, - * or as proxy object mapping such as in debugging. - *

- * - * This map permits null keys and values, and does not - * guarantee that elements will stay in the same order over time. The - * basic operations (get and put) 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. - *

- * - * This implementation is unsynchronized. If you want multi-thread - * access to be consistent, you must synchronize it, perhaps by using - * Collections.synchronizedMap(new IdentityHashMap(...));. - * The iterators are fail-fast, 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 - * @author Eric Blake - * @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 extends AbstractMap - implements Map, Serializable, Cloneable -{ - /** The default capacity. */ - private static final int DEFAULT_CAPACITY = 21; - - /** - * This object is used to mark deleted items. Package visible for use by - * nested classes. - */ - static final Object tombstone = new Object(); - - /** - * This object is used to mark empty slots. We need this because - * using null is ambiguous. Package visible for use by nested classes. - */ - static final Object emptyslot = 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 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[2 * max]; - Arrays.fill(table, emptyslot); - threshold = max / 4 * 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 m) - { - this(Math.max(m.size() * 2, DEFAULT_CAPACITY)); - putAll(m); - } - - /** - * Remove all mappings from this map. - */ - public void clear() - { - if (size != 0) - { - modCount++; - Arrays.fill(table, emptyslot); - 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 entry == key instead of - * entry == null ? key == null : entry.equals(key). - * - * @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) - { - return key == table[hash(key)]; - } - - /** - * Returns true if this HashMap contains the value. Unlike normal maps, - * this test uses entry == value instead of - * entry == null ? value == null : entry.equals(value). - * - * @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) - { - 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. - *

- * - * 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. The entries - * in this set are reference-based, rather than the normal object - * equality. Therefore, e1.equals(e2) returns - * e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue(), - * and e.hashCode() returns - * System.identityHashCode(e.getKey()) ^ - * System.identityHashCode(e.getValue()). - *

- * - * 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 entrySet() - { - if (entries == null) - entries = new AbstractSet() - { - public int size() - { - return size; - } - - public Iterator iterator() - { - return new IdentityIterator(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; - return m.getValue() == table[hash(m.getKey()) + 1]; - } - - public int hashCode() - { - return IdentityHashMap.this.hashCode(); - } - - public boolean remove(Object o) - { - if (! (o instanceof Map.Entry)) - return false; - Object key = ((Map.Entry) o).getKey(); - int h = hash(key); - if (table[h] == key) - { - size--; - modCount++; - table[h] = tombstone; - table[h + 1] = tombstone; - 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 - * this.entrySet().equals(m.entrySet()) 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

null
if the key maps to nothing. 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 entry == key instead of - * entry == null ? key == null : entry.equals(key). - * - * @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 Object get(Object key) - { - int h = hash(key); - return table[h] == key ? 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 == emptyslot || key == tombstone) - continue; - hash += (System.identityHashCode(key) - ^ System.identityHashCode(table[i + 1])); - } - return hash; - } - - /** - * Returns true if there are no key-value mappings currently in this Map - * @return size() == 0 - */ - 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. - *

- * - * 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. 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. - *

- * - * @return a set view of the keys - * @see #values() - * @see #entrySet() - */ - public Set keySet() - { - if (keys == null) - keys = new AbstractSet() - { - public int size() - { - return size; - } - - public Iterator iterator() - { - return new IdentityIterator(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 == emptyslot || key == tombstone) - continue; - hash += System.identityHashCode(key); - } - return hash; - - } - - public boolean remove(Object o) - { - int h = hash(o); - if (table[h] == o) - { - size--; - modCount++; - table[h] = tombstone; - table[h + 1] = tombstone; - 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 equals() - * 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 entry == key instead of - * entry == null ? key == null : entry.equals(key). - * - * @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 Object put(Object key, Object value) - { - // 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]; - Arrays.fill(table, emptyslot); - size = 0; - threshold = (table.length / 2) / 4 * 3; - - for (int i = old.length - 2; i >= 0; i -= 2) - { - Object oldkey = old[i]; - if (oldkey != tombstone && oldkey != emptyslot) - // Just use put. This isn't very efficient, but it is ok. - put(oldkey, old[i + 1]); - } - } - - int h = hash(key); - if (table[h] == key) - { - Object r = table[h + 1]; - table[h + 1] = value; - return r; - } - - // 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 m) - { - // Why did Sun specify this one? The superclass does the right thing. - super.putAll(m); - } - - /** - * 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

null
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 entry == key instead of - * entry == null ? key == null : entry.equals(key). - * - * @param key the key used to locate the value to remove - * @return whatever the key mapped to, if present - */ - public Object remove(Object key) - { - int h = hash(key); - if (table[h] == key) - { - modCount++; - size--; - Object r = table[h + 1]; - table[h] = tombstone; - table[h + 1] = tombstone; - return 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. - *

- * - * 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. - * Likewise, contains and remove go by == instead of equals(). - *

- * - * @return a bag view of the values - * @see #keySet() - * @see #entrySet() - */ - public Collection values() - { - if (values == null) - values = new AbstractCollection() - { - public int size() - { - return size; - } - - public Iterator iterator() - { - return new IdentityIterator(VALUES); - } - - public void clear() - { - IdentityHashMap.this.clear(); - } - - public boolean remove(Object o) - { - for (int i = table.length - 1; i > 0; i -= 2) - if (table[i] == o) - { - modCount++; - table[i - 1] = tombstone; - table[i] = tombstone; - size--; - return true; - } - return false; - } - }; - return values; - } - - /** - * Helper method which computes the hash code, then traverses the table - * until it finds the key, or the spot where the key would go. - * - * @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. - int hash(Object key) - { - // Implementation note: it is feasible for the table to have no - // emptyslots, if it is full with entries and tombstones, so we must - // remember where we started. If we encounter the key or an emptyslot, - // we are done. If we encounter a tombstone, the key may still be in - // the array. If we don't encounter the key, we use the first emptyslot - // or tombstone we encountered as the location where the key would go. - // By requiring at least 2 key/value slots, and rehashing at 75% - // capacity, we guarantee that there will always be either an emptyslot - // or a tombstone somewhere in the table. - int h = 2 * Math.abs(System.identityHashCode(key) % (table.length / 2)); - int del = -1; - int save = h; - - do - { - if (table[h] == key) - return h; - if (table[h] == emptyslot) - break; - if (table[h] == tombstone && del < 0) - del = h; - h -= 2; - if (h < 0) - h = table.length - 2; - } - while (h != save); - - return del < 0 ? h : del; - } - - /** - * 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 - * @author Eric Blake - */ - private final class IdentityIterator implements Iterator - { - /** - * 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 - * @throws ConcurrentModificationException if the Map was modified - */ - public boolean hasNext() - { - if (knownMod != modCount) - throw new ConcurrentModificationException(); - 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 Object next() - { - if (knownMod != modCount) - throw new ConcurrentModificationException(); - if (count == 0) - throw new NoSuchElementException(); - count--; - - Object key; - do - { - loc -= 2; - key = table[loc]; - } - while (key == emptyslot || key == tombstone); - - return type == KEYS ? key : (type == VALUES ? table[loc + 1] - : new IdentityEntry(loc)); - } - - /** - * Removes from the backing Map the last element which was fetched - * with the

next()
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 || table[loc] == tombstone) - throw new IllegalStateException(); - modCount++; - size--; - table[loc] = tombstone; - table[loc + 1] = tombstone; - 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 - */ - private final class IdentityEntry implements Map.Entry - { - /** 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 || table[loc] == tombstone) - throw new ConcurrentModificationException(); - if (! (o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry) o; - return table[loc] == e.getKey() && table[loc + 1] == 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 Object getKey() - { - if (knownMod != modCount || table[loc] == tombstone) - throw new ConcurrentModificationException(); - return 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 Object getValue() - { - if (knownMod != modCount || table[loc] == tombstone) - throw new ConcurrentModificationException(); - return 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 || table[loc] == tombstone) - throw new ConcurrentModificationException(); - return (System.identityHashCode(table[loc]) - ^ System.identityHashCode(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 Object setValue(Object value) - { - if (knownMod != modCount || table[loc] == tombstone) - throw new ConcurrentModificationException(); - Object r = table[loc + 1]; - table[loc + 1] = 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 final String toString() - { - if (knownMod != modCount || table[loc] == tombstone) - throw new ConcurrentModificationException(); - return table[loc] + "=" + 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[2 * Math.max(num * 2, DEFAULT_CAPACITY)]; - // Read key/value pairs. - while (--num >= 0) - put(s.readObject(), 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 != tombstone && key != emptyslot) - { - s.writeObject(key); - s.writeObject(table[i + 1]); - } - } - } -}