diff options
Diffstat (limited to 'libjava/java/util/IdentityHashMap.java')
-rw-r--r-- | libjava/java/util/IdentityHashMap.java | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/libjava/java/util/IdentityHashMap.java b/libjava/java/util/IdentityHashMap.java new file mode 100644 index 00000000000..374f09e70d1 --- /dev/null +++ b/libjava/java/util/IdentityHashMap.java @@ -0,0 +1,415 @@ +/* IdentityHashMap.java -- a class providing a hashtable data structure, + mapping Object --> Object, which uses object identity for hashing. + Copyright (C) 2001 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + +package java.util; + +import java.io.*; + +/** + * This class provides a hashtable-backed implementation of the + * Map interface. Unlike HashMap, it uses object identity to + * do its hashing. Also, it uses a linear-probe hash table. + * + * @author Tom Tromey <tromey@redhat.com> + * @since 1.4 + */ +public class IdentityHashMap extends AbstractMap + implements Map, Serializable, Cloneable +{ + private static final int DEFAULT_CAPACITY = 21; + + /** 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 + */ + public IdentityHashMap (int max) + { + if (max < 0) + throw new IllegalArgumentException (); + table = new Object[2 * max]; + Arrays.fill (table, emptyslot); + size = 0; + } + + /** 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. + */ + public IdentityHashMap (Map m) + { + int len = 2 * Math.max (m.size (), DEFAULT_CAPACITY); + table = new Object[len]; + Arrays.fill (table, emptyslot); + putAll (m); + } + + public void clear () + { + Arrays.fill (table, emptyslot); + size = 0; + } + + public Object clone () + { + IdentityHashMap copy = (IdentityHashMap) super.clone (); + copy.table = (Object[]) table.clone (); + return copy; + } + + public boolean containsKey (Object key) + { + int h = Math.abs (2 * System.identityHashCode (key) % table.length); + int save = h; + while (true) + { + if (table[h] == key) + return true; + if (table[h] == emptyslot) + return false; + h += 2; + if (h > table.length) + h = 0; + if (h == save) + return false; + } + } + + public boolean containsValue (Object value) + { + for (int i = 1; i < table.length; i += 2) + if (table[i] == value) + return true; + return false; + } + + public Set entrySet () + { + return new AbstractSet () + { + public int size () + { + return size; + } + + public Iterator iterator () + { + return new IdentityIterator (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 (IdentityHashMap.this.containsKey (m.getKey ()) + && IdentityHashMap.this.get (m.getKey ()) == m.getValue ()); + } + + public boolean remove (Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry m = (Map.Entry) o; + if (IdentityHashMap.this.containsKey (m.getKey ()) + && IdentityHashMap.this.get (m.getKey ()) == m.getValue ()) + { + int oldsize = size; + IdentityHashMap.this.remove (m.getKey ()); + return oldsize != size; + } + return false; + } + }; + } + + public Object get (Object key) + { + int h = Math.abs (2 * System.identityHashCode (key) % table.length); + int save = h; + while (true) + { + if (table[h] == key) + return table[h + 1]; + if (table[h] == emptyslot) + return null; + h += 2; + if (h > table.length) + h = 0; + if (h == save) + return null; + } + } + + public boolean isEmpty () + { + return size == 0; + } + + public Set keySet () + { + return new AbstractSet () + { + public int size () + { + return size; + } + + public Iterator iterator () + { + return new IdentityIterator (IdentityIterator.KEYS); + } + + public void clear () + { + IdentityHashMap.this.clear (); + } + + public boolean contains (Object o) + { + return IdentityHashMap.this.containsKey (o); + } + + public boolean remove (Object o) + { + int oldsize = size; + IdentityHashMap.this.remove (o); + return oldsize != size; + } + }; + } + + public Object put (Object key, Object value) + { + // Rehash is the load factor is too high. + if (size * 3 / 2 > table.length) + { + Object[] old = table; + table = new Object[old.length * 2]; + Arrays.fill (table, emptyslot); + size = 0; + for (int i = 0; i < old.length; ++i) + { + if (old[i] != tombstone && old[i] != emptyslot) + { + // Just use put. This isn't very efficient, but it is + // ok. + put (old[i], old[i + 1]); + } + } + } + + int h = Math.abs (2 * System.identityHashCode (key) % table.length); + int save = h; + int del = -1; + while (true) + { + if (table[h] == key) + { + Object r = table[h + 1]; + table[h + 1] = value; + return r; + } + else if (table[h] == tombstone && del == -1) + del = h; + else if (table[h] == emptyslot) + { + if (del == -1) + del = h; + break; + } + h += 2; + if (h > table.length) + h = 0; + if (h == save) + break; + } + + if (del != -1) + { + table[del] = key; + table[del + 1] = value; + ++size; + return null; + } + + // This is an error. + return null; + } + + public Object remove (Object key) + { + int h = Math.abs (2 * System.identityHashCode (key) % table.length); + int save = h; + while (true) + { + if (table[h] == key) + { + Object r = table[h + 1]; + table[h] = tombstone; + table[h + 1] = tombstone; + --size; + return r; + } + h += 2; + if (h > table.length) + h = 0; + if (h == save) + break; + } + + return null; + } + + public int size () + { + return size; + } + + public Collection values () + { + return new AbstractCollection () + { + public int size () + { + return size; + } + + public Iterator iterator () + { + return new IdentityIterator (IdentityIterator.VALUES); + } + + public void clear () + { + IdentityHashMap.this.clear (); + } + }; + } + + private class IdentityIterator implements Iterator + { + static final int KEYS = 0; + static final int VALUES = 1; + static final int ENTRIES = 2; + + // Type of iterator. + int type; + // Location in the table. + int loc; + // How many items we've seen. + int seen; + + IdentityIterator (int type) + { + this.type = type; + loc = 0; + seen = 0; + } + + public boolean hasNext () + { + return seen < size; + } + + public Object next () + { + while (true) + { + loc += 2; + if (loc >= table.length) + throw new NoSuchElementException (); + if (table[loc] != tombstone && table[loc] != emptyslot) + { + ++seen; + return table[loc]; + } + } + } + + public void remove () + { + if (loc >= table.length + || table[loc] == tombstone + || table[loc] == emptyslot) + throw new IllegalStateException (); + table[loc] = tombstone; + table[loc + 1] = tombstone; + --size; + } + } + + private void readObject (ObjectInputStream s) + throws IOException, ClassNotFoundException + { + int num = s.readInt (); + for (int i = 0; i < num; ++i) + { + Object key = s.readObject (); + Object value = s.readObject (); + put (key, value); + } + } + + private void writeObject (ObjectOutputStream s) + throws IOException + { + s.writeInt (size); + Iterator it = entrySet ().iterator (); + while (it.hasNext ()) + { + Map.Entry entry = (Map.Entry) it.next (); + s.writeObject (entry.getKey ()); + s.writeObject (entry.getValue ()); + } + } + + // Number of items in hash table. + private int size; + // The table itself. + private Object[] table; + + // This object is used to mark deleted items. + private Object tombstone = new Object (); + // This object is used to mark empty slots. We need this because + // using null is ambiguous. + private Object emptyslot = new Object (); +} |