Initial revision
From-SVN: r26263
This commit is contained in:
parent
140fa895c6
commit
ee9dd3721b
370 changed files with 173494 additions and 0 deletions
398
libjava/java/util/Hashtable.java
Normal file
398
libjava/java/util/Hashtable.java
Normal file
|
@ -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()
|
||||
// {
|
||||
// }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue