mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			399 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* Copyright (C) 1998, 1999  Red Hat, Inc.
 | |
| 
 | |
|    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()
 | |
|   // {
 | |
|   // }
 | |
| }
 |