home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / Hashtable.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  27.3 KB  |  918 lines

  1. /*
  2.  * @(#)Hashtable.java    1.62 98/03/18
  3.  *
  4.  * Copyright 1994-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util;
  16. import java.io.*;
  17.  
  18. /**
  19.  * This class implements a hashtable, which maps keys to values. Any 
  20.  * non-<code>null</code> object can be used as a key or as a value. 
  21.  * <p>
  22.  * To successfully store and retrieve objects from a hashtable, the 
  23.  * objects used as keys must implement the <code>hashCode</code> 
  24.  * method and the <code>equals</code> method. 
  25.  * <p>
  26.  * An instance of <code>Hashtable</code> has two parameters that 
  27.  * affect its efficiency: its <i>capacity</i> and its <i>load 
  28.  * factor</i>. The load factor should be between 0.0 and 1.0. When 
  29.  * the number of entries in the hashtable exceeds the product of the 
  30.  * load factor and the current capacity, the capacity is increased by 
  31.  * calling the <code>rehash</code> method. Larger load factors use 
  32.  * memory more efficiently, at the expense of larger expected time 
  33.  * per lookup. 
  34.  * <p>
  35.  * If many entries are to be made into a <code>Hashtable</code>, 
  36.  * creating it with a sufficiently large capacity may allow the 
  37.  * entries to be inserted more efficiently than letting it perform 
  38.  * automatic rehashing as needed to grow the table. 
  39.  * <p>
  40.  * This example creates a hashtable of numbers. It uses the names of 
  41.  * the numbers as keys: 
  42.  * <p><blockquote><pre>
  43.  *     Hashtable numbers = new Hashtable();
  44.  *     numbers.put("one", new Integer(1));
  45.  *     numbers.put("two", new Integer(2));
  46.  *     numbers.put("three", new Integer(3));
  47.  * </pre></blockquote>
  48.  * <p>
  49.  * To retrieve a number, use the following code: 
  50.  * <p><blockquote><pre>
  51.  *     Integer n = (Integer)numbers.get("two");
  52.  *     if (n != null) {
  53.  *         System.out.println("two = " + n);
  54.  *     }
  55.  * </pre></blockquote>
  56.  * <p>
  57.  * As of JDK1.2, this class has been retrofitted to implement Map,
  58.  * so that it becomes a part of Java's collection framework.  Unlike
  59.  * the new collection implementations, Vector is synchronized.
  60.  * <p>
  61.  * The Iterators returned by the iterator and listIterator methods
  62.  * of the Collections returned by all of Hashtable's "collection view methods"
  63.  * are <em>fail-fast</em>: if the Hashtable is structurally modified
  64.  * at any time after the Iterator is created, in any way except through the
  65.  * Iterator's own remove or add methods, the Iterator will throw a
  66.  * ConcurrentModificationException.  Thus, in the face of concurrent
  67.  * modification, the Iterator fails quickly and cleanly, rather than risking
  68.  * arbitrary, non-deterministic behavior at an undetermined time in the future.
  69.  * The Enumerations returned by Hashtable's keys and values methods are
  70.  * <em>not</em> fail-fast.
  71.  *
  72.  * @author  Arthur van Hoff
  73.  * @author  Josh Bloch
  74.  * @version 1.62, 03/18/98
  75.  * @see     Object#equals(java.lang.Object)
  76.  * @see     Object#hashCode()
  77.  * @see     Hashtable#rehash()
  78.  * @see     Collection
  79.  * @see        Map
  80.  * @see        HashMap
  81.  * @see        TreeMap
  82.  * @since   JDK1.0
  83.  */
  84. public class Hashtable extends Dictionary implements Map, Cloneable,
  85.                              java.io.Serializable {
  86.     /**
  87.      * The hash table data.
  88.      */
  89.     private transient Entry table[];
  90.  
  91.     /**
  92.      * The total number of entries in the hash table.
  93.      */
  94.     private transient int count;
  95.  
  96.     /**
  97.      * Rehashes the table when count exceeds this threshold.
  98.      */
  99.     private int threshold;
  100.  
  101.     /**
  102.      * The load factor for the hashtable.
  103.      */
  104.     private float loadFactor;
  105.  
  106.     /**
  107.      * The number of times this Hashtable has been structurally modified
  108.      * Structural modifications are those that change the number of entries in
  109.      * the Hashtable or otherwise modify its internal structure (e.g.,
  110.      * rehash).  This field is used to make iterators on Collection-views of
  111.      * the Hashtable fail-fast.  (See ConcurrentModificationException).
  112.      */
  113.     private transient int modCount = 0;
  114.  
  115.     /** use serialVersionUID from JDK 1.0.2 for interoperability */
  116.     private static final long serialVersionUID = 1421746759512286392L;
  117.  
  118.     /**
  119.      * Constructs a new, empty hashtable with the specified initial 
  120.      * capacity and the specified load factor.
  121.      *
  122.      * @param      initialCapacity   the initial capacity of the hashtable.
  123.      * @param      loadFactor        a number between 0.0 and 1.0.
  124.      * @exception  IllegalArgumentException  if the initial capacity is less
  125.      *               than zero, if the load factor is less than
  126.      *                or equal to zero, or if the load factor is greater than
  127.      *                one.
  128.      */
  129.     public Hashtable(int initialCapacity, float loadFactor) {
  130.     if (initialCapacity < 0)
  131.         throw new IllegalArgumentException("Illegal Capacity: "+
  132.                                                initialCapacity);
  133.         if ((loadFactor > 1) || (loadFactor <= 0))
  134.             throw new IllegalArgumentException("Illegal Load: "+loadFactor);
  135.     this.loadFactor = loadFactor;
  136.     table = new Entry[initialCapacity];
  137.     threshold = (int)(initialCapacity * loadFactor);
  138.     }
  139.  
  140.     /**
  141.      * Constructs a new, empty hashtable with the specified initial capacity
  142.      * and default load factor, which is <tt>0.75</tt>.
  143.      *
  144.      * @param   initialCapacity   the initial capacity of the hashtable.
  145.      * @exception IllegalArgumentException if the initial capacity is less
  146.      *              than zero
  147.      */
  148.     public Hashtable(int initialCapacity) {
  149.     this(initialCapacity, 0.75f);
  150.     }
  151.  
  152.     /**
  153.      * Constructs a new, empty hashtable with a default capacity and load
  154.      * factor, which is <tt>0.75</tt>. 
  155.      */
  156.     public Hashtable() {
  157.     this(101, 0.75f);
  158.     }
  159.  
  160.     /**
  161.      * Constructs a new hashtable with the same mappings as the given 
  162.      * Map.  The hashtable is created with a capacity of thrice the number
  163.      * of entries in the given Map or 11 (whichever is greater), and a
  164.      * default load factor.
  165.      *
  166.      * @since   JDK1.2
  167.      */
  168.     public Hashtable(Map t) {
  169.     this(Math.max(3*t.size(), 11), 0.75f);
  170.     putAll(t);
  171.     }
  172.  
  173.     /**
  174.      * Returns the number of keys in this hashtable.
  175.      *
  176.      * @return  the number of keys in this hashtable.
  177.      */
  178.     public int size() {
  179.     return count;
  180.     }
  181.  
  182.     /**
  183.      * Tests if this hashtable maps no keys to values.
  184.      *
  185.      * @return  <code>true</code> if this hashtable maps no keys to values;
  186.      *          <code>false</code> otherwise.
  187.      */
  188.     public boolean isEmpty() {
  189.     return count == 0;
  190.     }
  191.  
  192.     /**
  193.      * Returns an enumeration of the keys in this hashtable.
  194.      *
  195.      * @return  an enumeration of the keys in this hashtable.
  196.      * @see     Enumeration
  197.      * @see     #elements()
  198.      * @see    #keySet()
  199.      * @see    Map
  200.      */
  201.     public synchronized Enumeration keys() {
  202.     return new Enumerator(KEYS, false);
  203.     }
  204.  
  205.     /**
  206.      * Returns an enumeration of the values in this hashtable.
  207.      * Use the Enumeration methods on the returned object to fetch the elements
  208.      * sequentially.
  209.      *
  210.      * @return  an enumeration of the values in this hashtable.
  211.      * @see     java.util.Enumeration
  212.      * @see     #keys()
  213.      * @see    #values()
  214.      * @see    Map
  215.      */
  216.     public synchronized Enumeration elements() {
  217.     return new Enumerator(VALUES, false);
  218.     }
  219.  
  220.     /**
  221.      * Tests if some key maps into the specified value in this hashtable.
  222.      * This operation is more expensive than the <code>containsKey</code>
  223.      * method.
  224.      * <p>
  225.      * Note that this method is identical in functionality to containsValue,
  226.      * (which is part of the Map interface in the collections framework).
  227.      * 
  228.      * @param      value   a value to search for.
  229.      * @return     <code>true</code> if and only if some key maps to the
  230.      *             <code>value</code> argument in this hashtable as 
  231.      *             determined by the <tt>equals</tt> method;
  232.      *             <code>false</code> otherwise.
  233.      * @exception  NullPointerException  if the value is <code>null</code>.
  234.      * @see        #containsKey(Object)
  235.      * @see        #containsValue(Object)
  236.      * @see       Map
  237.      */
  238.     public synchronized boolean contains(Object value) {
  239.     if (value == null) {
  240.         throw new NullPointerException();
  241.     }
  242.  
  243.     Entry tab[] = table;
  244.     for (int i = tab.length ; i-- > 0 ;) {
  245.         for (Entry e = tab[i] ; e != null ; e = e.next) {
  246.         if (e.value.equals(value)) {
  247.             return true;
  248.         }
  249.         }
  250.     }
  251.     return false;
  252.     }
  253.  
  254.     /**
  255.      * Returns true if this Hashtable maps one or more keys to this value.
  256.      * <p>
  257.      * Note that this method is identical in functionality to contains
  258.      * (which predates the Map interface).
  259.      *
  260.      * @param value value whose presence in this Hashtable is to be tested.
  261.      * @see       Map
  262.      * @since JDK1.2
  263.      */
  264.     public boolean containsValue(Object value) {
  265.     return contains(value);
  266.     }
  267.  
  268.     /**
  269.      * Tests if the specified object is a key in this hashtable.
  270.      * 
  271.      * @param   key   possible key.
  272.      * @return  <code>true</code> if and only if the specified object 
  273.      *          is a key in this hashtable, as determined by the 
  274.      *          <tt>equals</tt> method; <code>false</code> otherwise.
  275.      * @see     #contains(Object)
  276.      */
  277.     public synchronized boolean containsKey(Object key) {
  278.     Entry tab[] = table;
  279.     int hash = key.hashCode();
  280.     int index = (hash & 0x7FFFFFFF) % tab.length;
  281.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  282.         if ((e.hash == hash) && e.key.equals(key)) {
  283.         return true;
  284.         }
  285.     }
  286.     return false;
  287.     }
  288.  
  289.     /**
  290.      * Returns the value to which the specified key is mapped in this hashtable.
  291.      *
  292.      * @param   key   a key in the hashtable.
  293.      * @return  the value to which the key is mapped in this hashtable;
  294.      *          <code>null</code> if the key is not mapped to any value in
  295.      *          this hashtable.
  296.      * @see     #put(Object, Object)
  297.      */
  298.     public synchronized Object get(Object key) {
  299.     Entry tab[] = table;
  300.     int hash = key.hashCode();
  301.     int index = (hash & 0x7FFFFFFF) % tab.length;
  302.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  303.         if ((e.hash == hash) && e.key.equals(key)) {
  304.         return e.value;
  305.         }
  306.     }
  307.     return null;
  308.     }
  309.  
  310.     /**
  311.      * Increases the capacity of and internally reorganizes this 
  312.      * hashtable, in order to accommodate and access its entries more 
  313.      * efficiently.  This method is called automatically when the 
  314.      * number of keys in the hashtable exceeds this hashtable's capacity 
  315.      * and load factor. 
  316.      */
  317.     protected void rehash() {
  318.     int oldCapacity = table.length;
  319.     Entry oldMap[] = table;
  320.  
  321.     int newCapacity = oldCapacity * 2 + 1;
  322.     Entry newMap[] = new Entry[newCapacity];
  323.  
  324.     modCount++;
  325.     threshold = (int)(newCapacity * loadFactor);
  326.     table = newMap;
  327.  
  328.     //System.out.println("rehash old=" + oldCapacity + ", new=" + newCapacity + ", thresh=" + threshold + ", count=" + count);
  329.  
  330.     for (int i = oldCapacity ; i-- > 0 ;) {
  331.         for (Entry old = oldMap[i] ; old != null ; ) {
  332.         Entry e = old;
  333.         old = old.next;
  334.  
  335.         int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  336.         e.next = newMap[index];
  337.         newMap[index] = e;
  338.         }
  339.     }
  340.     }
  341.  
  342.     /**
  343.      * Maps the specified <code>key</code> to the specified 
  344.      * <code>value</code> in this hashtable. Neither the key nor the 
  345.      * value can be <code>null</code>. 
  346.      * <p>
  347.      * The value can be retrieved by calling the <code>get</code> method 
  348.      * with a key that is equal to the original key. 
  349.      *
  350.      * @param      key     the hashtable key.
  351.      * @param      value   the value.
  352.      * @return     the previous value of the specified key in this hashtable,
  353.      *             or <code>null</code> if it did not have one.
  354.      * @exception  NullPointerException  if the key or value is
  355.      *               <code>null</code>.
  356.      * @see     Object#equals(Object)
  357.      * @see     #get(Object)
  358.      */
  359.     public synchronized Object put(Object key, Object value) {
  360.     // Make sure the value is not null
  361.     if (value == null) {
  362.         throw new NullPointerException();
  363.     }
  364.  
  365.     // Makes sure the key is not already in the hashtable.
  366.     Entry tab[] = table;
  367.     int hash = key.hashCode();
  368.     int index = (hash & 0x7FFFFFFF) % tab.length;
  369.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  370.         if ((e.hash == hash) && e.key.equals(key)) {
  371.         Object old = e.value;
  372.         e.value = value;
  373.         return old;
  374.         }
  375.     }
  376.  
  377.     modCount++;
  378.     if (count >= threshold) {
  379.         // Rehash the table if the threshold is exceeded
  380.         rehash();
  381.  
  382.             tab = table;
  383.             index = (hash & 0x7FFFFFFF) % tab.length;
  384.     } 
  385.  
  386.     // Creates the new entry.
  387.     Entry e = new Entry(hash, key, value, tab[index]);
  388.     tab[index] = e;
  389.     count++;
  390.     return null;
  391.     }
  392.  
  393.     /**
  394.      * Removes the key (and its corresponding value) from this 
  395.      * hashtable. This method does nothing if the key is not in the hashtable.
  396.      *
  397.      * @param   key   the key that needs to be removed.
  398.      * @return  the value to which the key had been mapped in this hashtable,
  399.      *          or <code>null</code> if the key did not have a mapping.
  400.      */
  401.     public synchronized Object remove(Object key) {
  402.     Entry tab[] = table;
  403.     int hash = key.hashCode();
  404.     int index = (hash & 0x7FFFFFFF) % tab.length;
  405.     for (Entry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
  406.         if ((e.hash == hash) && e.key.equals(key)) {
  407.         modCount++;
  408.         if (prev != null) {
  409.             prev.next = e.next;
  410.         } else {
  411.             tab[index] = e.next;
  412.         }
  413.         count--;
  414.         Object oldValue = e.value;
  415.         e.value = null;
  416.         return oldValue;
  417.         }
  418.     }
  419.     return null;
  420.     }
  421.  
  422.     /**
  423.      * Copies all of the mappings from the specified Map to this Hashtable
  424.      * These mappings will replace any mappings that this Hashtable had for any
  425.      * of the keys currently in the specified Map. 
  426.      *
  427.      * @since JDK1.2
  428.      */
  429.     public synchronized void putAll(Map t) {
  430.     Iterator i = t.entries().iterator();
  431.     while (i.hasNext()) {
  432.         Map.Entry e = (Map.Entry) i.next();
  433.         put(e.getKey(), e.getValue());
  434.     }
  435.     }
  436.  
  437.     /**
  438.      * Clears this hashtable so that it contains no keys. 
  439.      */
  440.     public synchronized void clear() {
  441.     Entry tab[] = table;
  442.     modCount++;
  443.     for (int index = tab.length; --index >= 0; )
  444.         tab[index] = null;
  445.     count = 0;
  446.     }
  447.  
  448.     /**
  449.      * Creates a shallow copy of this hashtable. All the structure of the 
  450.      * hashtable itself is copied, but the keys and values are not cloned. 
  451.      * This is a relatively expensive operation.
  452.      *
  453.      * @return  a clone of the hashtable.
  454.      */
  455.     public synchronized Object clone() {
  456.     try { 
  457.         Hashtable t = (Hashtable)super.clone();
  458.         t.table = new Entry[table.length];
  459.         for (int i = table.length ; i-- > 0 ; ) {
  460.         t.table[i] = (table[i] != null) 
  461.             ? (Entry)table[i].clone() : null;
  462.         }
  463.         t.keySet = null;
  464.         t.entries = null;
  465.             t.values = null;
  466.         t.modCount = 0;
  467.         return t;
  468.     } catch (CloneNotSupportedException e) { 
  469.         // this shouldn't happen, since we are Cloneable
  470.         throw new InternalError();
  471.     }
  472.     }
  473.  
  474.     /**
  475.      * Returns a string representation of this <tt>Hashtable</tt> object 
  476.      * in the form of a set of entries, enclosed in braces and separated 
  477.      * by the ASCII characters "<tt>, </tt>" (comma and space). Each 
  478.      * entry is rendered as the key, an equals sign <tt>=</tt>, and the 
  479.      * associated element, where the <tt>toString</tt> method is used to 
  480.      * convert the key and element to strings. <p>Overrides to 
  481.      * <tt>toString</tt> method of <tt>Object</tt>.
  482.      *
  483.      * @return  a string representation of this hashtable.
  484.      */
  485.     public synchronized String toString() {
  486.     int max = size() - 1;
  487.     StringBuffer buf = new StringBuffer();
  488.     Iterator it = entries().iterator();
  489.  
  490.     buf.append("{");
  491.     for (int i = 0; i <= max; i++) {
  492.         Entry e = (Entry) (it.next());
  493.         buf.append(e.key + "=" + e.value);
  494.         if (i < max)
  495.         buf.append(", ");
  496.     }
  497.     buf.append("}");
  498.     return buf.toString();
  499.     }
  500.  
  501.  
  502.     // Views
  503.  
  504.     private transient Set keySet = null;
  505.     private transient Set entries = null;
  506.     private transient Collection values = null;
  507.  
  508.     /**
  509.      * Returns a Set view of the keys contained in this Hashtable.  The Set
  510.      * is backed by the Hashtable, so changes to the Hashtable are reflected
  511.      * in the Set, and vice-versa.  The Set supports element removal
  512.      * (which removes the corresponding entry from the Hashtable), but not
  513.      * element addition.
  514.      *
  515.      * @since JDK1.2
  516.      */
  517.     public Set keySet() {
  518.     if (keySet == null) {
  519.         keySet = new AbstractSet() {
  520.         public Iterator iterator() {
  521.             return new Enumerator(KEYS, true);
  522.         }
  523.         public int size() {
  524.             return count;
  525.         }
  526.                 public boolean contains(Object o) {
  527.                     return containsKey(o);
  528.                 }
  529.         public boolean remove(Object o) {
  530.             return Hashtable.this.remove(o) != null;
  531.         }
  532.         public void clear() {
  533.             Hashtable.this.clear();
  534.         }
  535.         };
  536.     }
  537.     return keySet;
  538.     }
  539.  
  540.     /**
  541.      * Returns a Set view of the entries contained in this Hashtable.
  542.      * Each element in this collection is a Map.Entry.  The Set is
  543.      * backed by the Hashtable, so changes to the Hashtable are reflected in
  544.      * the Set, and vice-versa.  The Set supports element removal
  545.      * (which removes the corresponding entry from the Hashtable) via its
  546.      * Iterator, but not element addition or "direct" element removal.
  547.      *
  548.      * @see   Map.Entry
  549.      * @since JDK1.2
  550.      */
  551.     public Set entries() {
  552.     if (entries==null) {
  553.         entries = new AbstractSet() {
  554.                 public Iterator iterator() {
  555.                     return new Enumerator(ENTRIES, true);
  556.                 }
  557.  
  558.                 public boolean contains(Object o) {
  559.                     if (!(o instanceof Map.Entry))
  560.                         return false;
  561.                     Map.Entry entry = (Map.Entry)o;
  562.                     Object key = entry.getKey();
  563.                     Entry tab[] = table;
  564.                     int hash = key.hashCode();
  565.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  566.  
  567.                     for (Entry e = tab[index]; e != null; e = e.next)
  568.                         if (e.hash==hash && e.equals(entry))
  569.                             return true;
  570.                     return false;
  571.                 }
  572.  
  573.         public boolean remove(Object o) {
  574.                     if (!(o instanceof Map.Entry))
  575.                         return false;
  576.                     Map.Entry entry = (Map.Entry)o;
  577.                     Object key = entry.getKey();
  578.                     Entry tab[] = table;
  579.                     int hash = key.hashCode();
  580.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  581.  
  582.                     for (Entry e = tab[index], prev = null; e != null;
  583.                          prev = e, e = e.next) {
  584.                         if (e.hash==hash && e.equals(entry)) {
  585.                             modCount++;
  586.                             if (prev != null)
  587.                                 prev.next = e.next;
  588.                             else
  589.                                 tab[index] = e.next;
  590.  
  591.                             count--;
  592.                             e.value = null;
  593.                             return true;
  594.                         }
  595.                     }
  596.                     return false;
  597.                 }
  598.  
  599.                 public int size() {
  600.                     return count;
  601.                 }
  602.  
  603.                 public void clear() {
  604.                     Hashtable.this.clear();
  605.                 }
  606.             };
  607.         }
  608.  
  609.     return entries;
  610.     }
  611.  
  612.     /**
  613.      * Returns a Collection view of the values contained in this Hashtable.
  614.      * The Collection is backed by the Hashtable, so changes to the Hashtable
  615.      * are reflected in the Collection, and vice-versa.  The Collection
  616.      * supports element removal (which removes the corresponding entry from
  617.      * the Hashtable) via its Iterator, but not element addition or "direct"
  618.      * element removal.
  619.      *
  620.      * @since JDK1.2
  621.      */
  622.     public Collection values() {
  623.     if (values==null) {
  624.         values = new AbstractCollection() {
  625.                 public Iterator iterator() {
  626.                     return new Enumerator(VALUES, true);
  627.                 }
  628.                 public int size() {
  629.                     return count;
  630.                 }
  631.                 public boolean contains(Object o) {
  632.                     return containsValue(o);
  633.                 }
  634.                 public void clear() {
  635.                     Hashtable.this.clear();
  636.                 }
  637.             };
  638.         }
  639.         return values;
  640.     }
  641.  
  642.     // Comparison and hashing
  643.  
  644.     /**
  645.      * Compares the specified Object with this Map for equality,
  646.      * as per the definition in the Map interface.
  647.      *
  648.      * @return true if the specified Object is equal to this Map.
  649.      * @see Map#equals(Object)
  650.      * @since JDK1.2
  651.      */
  652.     public boolean equals(Object o) {
  653.     if (o == this)
  654.         return true;
  655.  
  656.     if (!(o instanceof Map))
  657.         return false;
  658.     Map t = (Map) o;
  659.     if (t.size() != size())
  660.         return false;
  661.  
  662.     Iterator i = entries().iterator();
  663.     while (i.hasNext()) {
  664.         Entry e = (Entry) i.next();
  665.         Object key = e.getKey();
  666.         Object value = e.getValue();
  667.         if (value == null) {
  668.         if (!(t.get(key)==null && t.containsKey(key)))
  669.             return false;
  670.         } else {
  671.         if (!value.equals(t.get(key)))
  672.             return false;
  673.         }
  674.     }
  675.     return true;
  676.     }
  677.  
  678.     /**
  679.      * Returns the hash code value for this Map as per the definition in the
  680.      * Map interface.
  681.      *
  682.      * @see Map#hashCode()
  683.      * @since JDK1.2
  684.      */
  685.     public int hashCode() {
  686.     int h = 0;
  687.     Iterator i = entries().iterator();
  688.     while (i.hasNext())
  689.         h += i.next().hashCode();
  690.     return h;
  691.     }
  692.  
  693.     /**
  694.      * WriteObject is called to save the state of the hashtable to a stream.
  695.      * Only the keys and values are serialized since the hash values may be
  696.      * different when the contents are restored.
  697.      * iterate over the contents and write out the keys and values.
  698.      */
  699.     private synchronized void writeObject(java.io.ObjectOutputStream s)
  700.         throws IOException
  701.     {
  702.     // Write out the length, threshold, loadfactor
  703.     s.defaultWriteObject();
  704.  
  705.     // Write out length, count of elements and then the key/value objects
  706.     s.writeInt(table.length);
  707.     s.writeInt(count);
  708.     for (int index = table.length-1; index >= 0; index--) {
  709.         Entry entry = table[index];
  710.  
  711.         while (entry != null) {
  712.         s.writeObject(entry.key);
  713.         s.writeObject(entry.value);
  714.         entry = entry.next;
  715.         }
  716.     }
  717.     }
  718.  
  719.     /**
  720.      * readObject is called to restore the state of the hashtable from
  721.      * a stream.  Only the keys and values are serialized since the
  722.      * hash values may be different when the contents are restored.
  723.      * Read count elements and insert into the hashtable. 
  724.      */
  725.     private synchronized void readObject(java.io.ObjectInputStream s)
  726.          throws IOException, ClassNotFoundException
  727.     {
  728.     // Read in the length, threshold, and loadfactor
  729.     s.defaultReadObject();
  730.  
  731.     // Read the original length of the array and number of elements
  732.     int origlength = s.readInt();
  733.     int elements = s.readInt();
  734.  
  735.     // Compute new size with a bit of room 5% to grow but
  736.     // No larger than the original size.  Make the length
  737.     // odd if it's large enough, this helps distribute the entries.
  738.     // Guard against the length ending up zero, that's not valid.
  739.     int length = (int)(elements * loadFactor) + (elements / 20) + 3;
  740.     if (length > elements && (length & 1) == 0)
  741.         length--;
  742.     if (origlength > 0 && length > origlength)
  743.         length = origlength;
  744.  
  745.     table = new Entry[length];
  746.     count = 0;
  747.  
  748.     // Read the number of elements and then all the key/value objects
  749.     for (; elements > 0; elements--) {
  750.         Object key = s.readObject();
  751.         Object value = s.readObject();
  752.         put(key, value);
  753.     }
  754.     }
  755.  
  756.  
  757.     /**
  758.      * Hashtable collision list.
  759.      */
  760.     private static class Entry implements Map.Entry {
  761.     int hash;
  762.     Object key;
  763.     Object value;
  764.     Entry next;
  765.  
  766.     protected Entry(int hash, Object key, Object value, Entry next) {
  767.         this.hash = hash;
  768.         this.key = key;
  769.         this.value = value;
  770.         this.next = next;
  771.     }
  772.  
  773.     protected Object clone() {
  774.         return new Entry(hash, key, value,
  775.                  (next==null ? null : (Entry)next.clone()));
  776.     }
  777.  
  778.     // Map.Entry Ops 
  779.  
  780.     public Object getKey() {
  781.         return key;
  782.     }
  783.  
  784.     public Object getValue() {
  785.         return value;
  786.     }
  787.  
  788.     public Object setValue(Object value) {
  789.         if (value == null)
  790.         throw new NullPointerException();
  791.  
  792.         Object oldValue = this.value;
  793.         this.value = value;
  794.         return oldValue;
  795.     }
  796.  
  797.     public boolean equals(Object o) {
  798.         if (!(o instanceof Map.Entry))
  799.         return false;
  800.         Map.Entry e = (Map.Entry)o;
  801.  
  802.         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  803.            (value==null ? e.getValue()==null : value.equals(e.getValue()));
  804.     }
  805.  
  806.     public int hashCode() {
  807.         return hash ^ (value==null ? 0 : value.hashCode());
  808.     }
  809.  
  810.     public String toString() {
  811.         return key.toString()+"="+value.toString();
  812.     }
  813.     }
  814.  
  815.     // Types of Enumerations/Iterations
  816.     private static final int KEYS = 0;
  817.     private static final int VALUES = 1;
  818.     private static final int ENTRIES = 2;
  819.  
  820.     /**
  821.      * A hashtable enumerator class.  This class implements both the
  822.      * Enumeration and Iterator interfaces, but individual instances
  823.      * can be created with the Iterator methods disabled.  This is necessary
  824.      * to avoid unintentionally increasing the capabilities granted a user
  825.      * by passing an Enumeration.
  826.      */
  827.     private class Enumerator implements Enumeration, Iterator {
  828.     Entry[] table = Hashtable.this.table;
  829.     int index = table.length;
  830.     Entry entry = null;
  831.     Entry lastReturned = null;
  832.     int type;
  833.  
  834.     /**
  835.      * Indicates whether this Enumerator is serving as an Iterator
  836.      * or an Enumeration.  (true -> Iterator).
  837.      */
  838.     boolean iterator;
  839.  
  840.     /**
  841.      * The modCount value that the iterator believes that the backing
  842.      * List should have.  If this expectation is violated, the iterator
  843.      * has detected concurrent modification.
  844.      */
  845.     private int expectedModCount = modCount;
  846.  
  847.     Enumerator(int type, boolean iterator) {
  848.         this.type = type;
  849.         this.iterator = iterator;
  850.     }
  851.  
  852.     public boolean hasMoreElements() {
  853.         if (entry != null) {
  854.         return true;
  855.         }
  856.         while (index-- > 0) {
  857.         if ((entry = table[index]) != null) {
  858.             return true;
  859.         }
  860.         }
  861.         return false;
  862.     }
  863.  
  864.     public Object nextElement() {
  865.         if (entry == null) {
  866.         while ((index-- > 0) && ((entry = table[index]) == null));
  867.         }
  868.         if (entry != null) {
  869.         Entry e = lastReturned = entry;
  870.         entry = e.next;
  871.         return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  872.         }
  873.         throw new NoSuchElementException("Hashtable Enumerator");
  874.     }
  875.  
  876.     // Iterator methods
  877.     public boolean hasNext() {
  878.         return hasMoreElements();
  879.     }
  880.  
  881.     public Object next() {
  882.         if (modCount != expectedModCount)
  883.         throw new ConcurrentModificationException();
  884.         return nextElement();
  885.     }
  886.  
  887.     public void remove() {
  888.         if (!iterator)
  889.         throw new UnsupportedOperationException();
  890.         if (lastReturned == null)
  891.         throw new IllegalStateException("Hashtable Enumerator");
  892.         if (modCount != expectedModCount)
  893.         throw new ConcurrentModificationException();
  894.  
  895.         synchronized(Hashtable.this) {
  896.         Entry[] tab = Hashtable.this.table;
  897.         int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  898.  
  899.         for (Entry e = tab[index], prev = null; e != null;
  900.              prev = e, e = e.next) {
  901.             if (e == lastReturned) {
  902.             modCount++;
  903.             expectedModCount++;
  904.             if (prev == null)
  905.                 tab[index] = e.next;
  906.             else
  907.                 prev.next = e.next;
  908.             count--;
  909.             lastReturned = null;
  910.             return;
  911.             }
  912.         }
  913.         throw new ConcurrentModificationException();
  914.         }
  915.     }
  916.     }
  917. }
  918.