aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/util/Hashtable.java
diff options
context:
space:
mode:
authorTom Tromey <tromey@gcc.gnu.org>1999-04-07 14:42:40 +0000
committerTom Tromey <tromey@gcc.gnu.org>1999-04-07 14:42:40 +0000
commitee9dd3721be68b9fa63dea9aa5a1d86e66958cde (patch)
treed96801a16fdf03a5682ef98730fe333a46eef944 /libjava/java/util/Hashtable.java
parent140fa895c6b859f827fc4437b91775a82cd105fb (diff)
downloadgcc-ee9dd3721be68b9fa63dea9aa5a1d86e66958cde.zip
gcc-ee9dd3721be68b9fa63dea9aa5a1d86e66958cde.tar.gz
gcc-ee9dd3721be68b9fa63dea9aa5a1d86e66958cde.tar.bz2
Initial revision
From-SVN: r26263
Diffstat (limited to 'libjava/java/util/Hashtable.java')
-rw-r--r--libjava/java/util/Hashtable.java398
1 files changed, 398 insertions, 0 deletions
diff --git a/libjava/java/util/Hashtable.java b/libjava/java/util/Hashtable.java
new file mode 100644
index 0000000..51ab2b1
--- /dev/null
+++ b/libjava/java/util/Hashtable.java
@@ -0,0 +1,398 @@
+/* Copyright (C) 1998, 1999 Cygnus Solutions
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+package java.util;
+
+import java.io.Serializable;
+
+/**
+ * @author Warren Levy <warrenl@cygnus.com>
+ * @date September 24, 1998.
+ */
+/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
+ * "The Java Language Specification", ISBN 0-201-63451-1
+ * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
+ * Status: Believed complete and correct
+ */
+
+class HashtableEntry
+{
+ public Object key;
+ public Object value;
+ public HashtableEntry nextEntry = null;
+
+ public HashtableEntry(Object key, Object value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+}
+
+class HashtableEnumeration implements Enumeration
+{
+ // TBD: Enumeration is not safe if new elements are put in the table as
+ // this could cause a rehash and we'd completely lose our place. Even
+ // without a rehash, it is undetermined if a new element added would
+ // appear in the enumeration. The spec says nothing about this, but
+ // the "Java Class Libraries" book infers that modifications to the
+ // hashtable during enumeration causes indeterminate results. Don't do it!
+ // A safer way would be to make a copy of the table (e.g. into a vector)
+ // but this is a fair bit more expensive.
+ private HashtableEntry[] bucket;
+ private int bucketIndex;
+ private HashtableEntry elem;
+ private int enumCount;
+ private int size;
+ private boolean values;
+
+ public HashtableEnumeration(HashtableEntry[] bkt, int sz, boolean isValues)
+ {
+ bucket = bkt;
+ bucketIndex = -1;
+ enumCount = 0;
+ elem = null;
+ size = sz;
+ values = isValues;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return enumCount < size;
+ }
+
+ public Object nextElement()
+ {
+ if (!hasMoreElements())
+ throw new NoSuchElementException();
+
+ // Find next element
+ if (elem != null) // In the middle of a bucket
+ elem = elem.nextEntry;
+ while (elem == null) // Find the next non-empty bucket
+ elem = bucket[++bucketIndex];
+
+ enumCount++;
+ return values ? elem.value : elem.key;
+ }
+}
+
+// TBD: The algorithm used here closely reflects what is described in
+// the "Java Class Libraries" book. The "Java Language Spec" is much
+// less specific about the implementation. Because of this freedom
+// provided by the actual spec, hash table algorithms should be
+// investigated to see if there is a better alternative to this one.
+
+// TODO12:
+// public class Hashtable extends Dictionary
+// implements Map, Cloneable, Serializable
+
+public class Hashtable extends Dictionary implements Cloneable, Serializable
+{
+ private HashtableEntry bucket[];
+ private float loadFactor;
+ private int hsize = 0;
+
+ public Hashtable()
+ {
+ // The "Java Class Libraries" book (p. 919) says that initial size in this
+ // case is 101 (a prime number to increase the odds of even distribution).
+ this(101, 0.75F);
+ }
+
+ public Hashtable(int initialSize)
+ {
+ this(initialSize, 0.75F);
+ }
+
+ public Hashtable(int initialSize, float loadFactor)
+ {
+ if (initialSize < 0 || loadFactor <= 0.0 || loadFactor > 1.0)
+ throw new IllegalArgumentException();
+
+ bucket = new HashtableEntry[initialSize];
+ this.loadFactor = loadFactor;
+ }
+
+ // TODO12:
+ // public Hashtable(Map t)
+ // {
+ // }
+
+ public synchronized void clear()
+ {
+ // Aid the GC by nulling out the entries in the hash table.
+ for (int i = 0; i < bucket.length; i++)
+ {
+ HashtableEntry elem = bucket[i];
+ bucket[i] = null; // May already be null.
+ while (elem != null)
+ {
+ HashtableEntry next = elem.nextEntry;
+ elem.nextEntry = null; // May already be null.
+ elem = next;
+ }
+ }
+ hsize = 0;
+ }
+
+ public synchronized Object clone()
+ {
+ // New hashtable will have same initialCapacity and loadFactor.
+ Hashtable newTable = new Hashtable(bucket.length, loadFactor);
+
+ HashtableEntry newElem, prev = null;
+ for (int i = 0; i < bucket.length; i++)
+ for (HashtableEntry elem = bucket[i]; elem != null; elem = elem.nextEntry)
+ {
+ // An easy but expensive method is newTable.put(elem.key, elem.value);
+ // Since the hash tables are the same size, the buckets and collisions
+ // will be the same in the new one, so we can just clone directly.
+ // This is much cheaper than using put.
+ newElem = new HashtableEntry(elem.key, elem.value);
+ if (newTable.bucket[i] == null)
+ prev = newTable.bucket[i] = newElem;
+ else
+ prev = prev.nextEntry = newElem;
+ }
+
+ newTable.hsize = this.hsize;
+ return newTable;
+ }
+
+ public synchronized boolean contains(Object value) throws NullPointerException
+ {
+ // An exception is thrown here according to the JDK 1.2 doc.
+ if (value == null)
+ throw new NullPointerException();
+
+ for (int i = 0; i < bucket.length; i++)
+ for (HashtableEntry elem = bucket[i]; elem != null; elem = elem.nextEntry)
+ if (elem.value.equals(value))
+ return true;
+
+ return false;
+ }
+
+ public synchronized boolean containsKey(Object key)
+ {
+ // The Map interface mandates that we throw this.
+ if (key == null)
+ throw new NullPointerException ();
+
+ for (HashtableEntry elem = bucket[Math.abs(key.hashCode()
+ % bucket.length)];
+ elem != null; elem = elem.nextEntry)
+ if (elem.key.equals(key))
+ return true;
+
+ return false;
+ }
+
+ public synchronized Enumeration elements()
+ {
+ return new HashtableEnumeration(bucket, hsize, true);
+ }
+
+ public synchronized Object get(Object key)
+ {
+ // The Dictionary interface mandates that get() throw a
+ // NullPointerException if key is null.
+ if (key == null)
+ throw new NullPointerException ();
+
+ for (HashtableEntry elem = bucket[Math.abs (key.hashCode()
+ % bucket.length)];
+ elem != null; elem = elem.nextEntry)
+ if (elem.key.equals(key))
+ return elem.value;
+
+ return null;
+ }
+
+ public boolean isEmpty()
+ {
+ return this.hsize <= 0;
+ }
+
+ public synchronized Enumeration keys()
+ {
+ return new HashtableEnumeration(bucket, hsize, false);
+ }
+
+ public synchronized Object put(Object key, Object value)
+ throws NullPointerException
+ {
+ if (key == null || value == null)
+ throw new NullPointerException();
+
+ HashtableEntry prevElem = null;
+ final int index = Math.abs(key.hashCode() % bucket.length);
+
+ for (HashtableEntry elem = bucket[index]; elem != null;
+ prevElem = elem, elem = elem.nextEntry)
+ if (elem.key.equals(key))
+ {
+ // Update with the new value and then return the old one.
+ Object oldVal = elem.value;
+ elem.value = value;
+ return oldVal;
+ }
+
+ // At this point, we know we need to add a new element.
+ HashtableEntry newElem = new HashtableEntry(key, value);
+ if (bucket[index] == null)
+ bucket[index] = newElem;
+ else
+ prevElem.nextEntry = newElem;
+
+ if (++hsize > loadFactor * bucket.length)
+ rehash();
+
+ return null;
+ }
+
+ protected void rehash()
+ {
+ // Create a new table which is twice the size (plus one) of the old.
+ // One is added to make the new array length odd so it thus has at least
+ // a (small) possibility of being a prime number.
+ HashtableEntry oldBucket[] = bucket;
+ bucket = new HashtableEntry[bucket.length * 2 + 1];
+
+ // Copy over each entry into the new table
+ HashtableEntry elem;
+ for (int i = 0; i < oldBucket.length; i++)
+ for (elem = oldBucket[i]; elem != null; elem = elem.nextEntry)
+ {
+ // Calling put(elem.key, elem.value); would seem like the easy way
+ // but it is dangerous since put increases 'hsize' and calls rehash!
+ // This could become infinite recursion under the right
+ // circumstances. Instead, we'll add the element directly; this is a
+ // bit more efficient than put since the data is already verified.
+ final int index = Math.abs(elem.key.hashCode() % bucket.length);
+ HashtableEntry newElem = new HashtableEntry(elem.key, elem.value);
+ if (bucket[index] == null)
+ bucket[index] = newElem;
+ else
+ {
+ // Since this key can't already be in the table, just add this
+ // in at the top of the bucket.
+ newElem.nextEntry = bucket[index];
+ bucket[index] = newElem;
+ }
+ }
+ }
+
+ public synchronized Object remove(Object key)
+ {
+ // TBD: Hmm, none of the various docs say to throw an exception here.
+ if (key == null)
+ return null;
+
+ Object retval;
+ HashtableEntry prevElem = null;
+ final int index = Math.abs(key.hashCode() % bucket.length);
+
+ for (HashtableEntry elem = bucket[index]; elem != null;
+ prevElem = elem, elem = elem.nextEntry)
+ if (elem.key.equals(key))
+ {
+ retval = elem.value;
+ if (prevElem == null)
+ bucket[index] = elem.nextEntry;
+ else
+ prevElem.nextEntry = elem.nextEntry;
+ --hsize;
+ return retval;
+ }
+
+ return null;
+ }
+
+ public int size()
+ {
+ return this.hsize;
+ }
+
+ public synchronized String toString()
+ {
+ // Following the Java Lang Spec 21.5.4 (p. 636).
+
+ Enumeration keys = keys();
+ Enumeration values = elements();
+
+ // Prepend first element with open bracket
+ StringBuffer result = new StringBuffer("{");
+
+ // add first element if one exists
+ // TBD: Seems like it is more efficient to catch the exception than
+ // to call hasMoreElements each time around.
+ try
+ {
+ result.append(keys.nextElement().toString() + "=" +
+ values.nextElement().toString());
+ }
+ catch (NoSuchElementException ex)
+ {
+ }
+
+ // Prepend subsequent elements with ", "
+ try
+ {
+ while (true)
+ result.append(", " + keys.nextElement().toString() + "=" +
+ values.nextElement().toString());
+ }
+ catch (NoSuchElementException ex)
+ {
+ }
+
+ // Append last element with closing bracket
+ result.append("}");
+ return result.toString();
+ }
+
+ // TODO12:
+ // public Set entrySet()
+ // {
+ // }
+
+ // TODO12:
+ // public Set keySet()
+ // {
+ // }
+
+ // Since JDK 1.2:
+ // This method is identical to contains but is part of the 1.2 Map interface.
+ // TBD: Should contains return containsValue instead? Depends on which
+ // will be called more typically.
+ public synchronized boolean containsValue(Object value)
+ {
+ return this.contains(value);
+ }
+
+ // TODO12:
+ // public boolean equals(Object o)
+ // {
+ // }
+
+ // TODO12:
+ // public boolean hashCode()
+ // {
+ // }
+
+ // TODO12:
+ // public void putAll(Map t)
+ // {
+ // }
+
+ // TODO12:
+ // public Collection values()
+ // {
+ // }
+}