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

  1. /*
  2.  * @(#)HashMap.java    1.17 98/03/18
  3.  *
  4.  * Copyright 1997, 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.  * Hash-table based implementation of the Map interface.  This implementation
  20.  * provides all of the optional Map operations, and permits null values
  21.  * and the null key.  (HashMap is roughly equivalent to Hashtable, except
  22.  * that it is unsynchronized and permits nulls.)  This class makes
  23.  * no guarantees as to the order of the Map; in particular, it does not
  24.  * guarantee that the order will remain constant over time.
  25.  * <p>
  26.  * This implementation provides constant-time performance for the basic
  27.  * operations (get and put), assuming the the hash function disperses
  28.  * the elements properly among the buckets.  Iteration requires time
  29.  * proportional to the number of buckets in the hash table plus
  30.  * the size of the Map (the number of key-value mappings).
  31.  * <p>
  32.  * An instance of HashMap has two parameters that affect its efficiency: its
  33.  * <i>capacity</i> and its <i>load factor</i>. The load factor should be
  34.  * between 0.0 and 1.0. When the number of entries in the HashMap exceeds
  35.  * the product of the load factor and the current capacity, the capacity is
  36.  * increased by calling the rehash method. Larger load factors
  37.  * use memory more efficiently, at the expense of larger expected time per
  38.  * lookup.
  39.  * <p>
  40.  * If many entries are to be made into a HashMap, creating it with a
  41.  * sufficiently large capacity will allow the entries to be inserted more
  42.  * efficiently than letting it perform automatic rehashing as needed to grow
  43.  * the table.
  44.  * <p>
  45.  * <strong>Note that this implementation is not synchronized.</strong> If
  46.  * multiple threads access a HashMap concurrently, and at least one of the
  47.  * threads modifies the HashMap structurally, it <em>must</em> be synchronized
  48.  * externally.  (A structural modification is any operation that adds or
  49.  * deletes one or more mappings; merely changing the value associated
  50.  * with a key that is already contained in the Table is not a structural
  51.  * modification.)  This is typically accomplished by synchronizing on some
  52.  * object that naturally encapsulates the HashMap.  If no such object exists,
  53.  * the HashMap should be "wrapped" using the Collections.synchronizedSet
  54.  * method.  This is best done at creation time, to prevent accidental
  55.  * unsynchronized access to the HashMap:
  56.  * <pre>
  57.  *    Map m = Collections.synchronizedMap(new HashMap(...));
  58.  * </pre>
  59.  * <p>
  60.  * The Iterators returned by the iterator methods of the Collections returned
  61.  * by all of HashMap's "collection view methods" are <em>fail-fast</em>: if the
  62.  * HashMap is structurally modified at any time after the Iterator is created,
  63.  * in any way except through the Iterator's own remove or add methods, the
  64.  * Iterator will throw a ConcurrentModificationException.  Thus, in the face of
  65.  * concurrent modification, the Iterator fails quickly and cleanly, rather than
  66.  * risking arbitrary, non-deterministic behavior at an undetermined time in the
  67.  * future.
  68.  *
  69.  * @author  Josh Bloch
  70.  * @author  Arthur van Hoff
  71.  * @version 1.17, 03/18/98
  72.  * @see     Object#hashCode()
  73.  * @see     Collection
  74.  * @see        Map
  75.  * @see        TreeMap
  76.  * @see        Hashtable
  77.  * @since   JDK1.2
  78.  */
  79.  
  80. public class HashMap extends AbstractMap implements Map, Cloneable,
  81.                      java.io.Serializable {
  82.     /**
  83.      * The hash table data.
  84.      */
  85.     private transient Entry table[];
  86.  
  87.     /**
  88.      * The total number of entries in the hash table.
  89.      */
  90.     private transient int count;
  91.  
  92.     /**
  93.      * Rehashes the table when count exceeds this threshold.
  94.      */
  95.     private int threshold;
  96.  
  97.     /**
  98.      * The load factor for the HashMap.
  99.      */
  100.     private float loadFactor;
  101.  
  102.     /**
  103.      * The number of times this HashMap has been structurally modified
  104.      * Structural modifications are those that change the number of entries in
  105.      * the HashMap or otherwise modify its internal structure (e.g.,
  106.      * rehash).  This field is used to make iterators on Collection-views of
  107.      * the HashMap fail-fast.  (See ConcurrentModificationException).
  108.      */
  109.     private transient int modCount = 0;
  110.  
  111.     /**
  112.      * Constructs a new, empty HashMap with the specified initial 
  113.      * capacity and the specified load factor. 
  114.      *
  115.      * @param      initialCapacity   the initial capacity of the HashMap.
  116.      * @param      loadFactor        a number between 0.0 and 1.0.
  117.      * @exception  IllegalArgumentException  if the initial capacity is less
  118.      *               than or equal to zero, or if the load factor is less than
  119.      *               or equal to zero.
  120.      */
  121.     public HashMap(int initialCapacity, float loadFactor) {
  122.     if (initialCapacity < 0)
  123.         throw new IllegalArgumentException("Illegal Initial Capacity: "+
  124.                                                initialCapacity);
  125.         if ((loadFactor > 1) || (loadFactor <= 0))
  126.             throw new IllegalArgumentException("Illegal Load factor: "+
  127.                                                loadFactor);
  128.     this.loadFactor = loadFactor;
  129.     table = new Entry[initialCapacity];
  130.     threshold = (int)(initialCapacity * loadFactor);
  131.     }
  132.  
  133.     /**
  134.      * Constructs a new, empty HashMap with the specified initial capacity
  135.      * and default load factor.
  136.      *
  137.      * @param   initialCapacity   the initial capacity of the HashMap.
  138.      */
  139.     public HashMap(int initialCapacity) {
  140.     this(initialCapacity, 0.75f);
  141.     }
  142.  
  143.     /**
  144.      * Constructs a new, empty HashMap with a default capacity and load
  145.      * factor. 
  146.      */
  147.     public HashMap() {
  148.     this(101, 0.75f);
  149.     }
  150.  
  151.     /**
  152.      * Constructs a new HashMap with the same mappings as the given 
  153.      * Map.  The HashMap is created with a capacity of thrice the number
  154.      * of entries in the given Map or 11 (whichever is greater), and a
  155.      * default load factor.
  156.      */
  157.     public HashMap(Map t) {
  158.     this(Math.max(3*t.size(), 11), 0.75f);
  159.     putAll(t);
  160.     }
  161.  
  162.     /**
  163.      * Returns the number of key-value mappings in this Map.
  164.      */
  165.     public int size() {
  166.     return count;
  167.     }
  168.  
  169.     /**
  170.      * Returns true if this Map contains no key-value mappings.
  171.      */
  172.     public boolean isEmpty() {
  173.     return count == 0;
  174.     }
  175.  
  176.     /**
  177.      * Returns true if this HashMap maps one or more keys to the specified
  178.      * value.
  179.      *
  180.      * @param value value whose presence in this Map is to be tested.
  181.      */
  182.     public boolean containsValue(Object value) {
  183.     Entry tab[] = table;
  184.  
  185.     if (value==null) {
  186.         for (int i = tab.length ; i-- > 0 ;)
  187.         for (Entry e = tab[i] ; e != null ; e = e.next)
  188.             if (e.value==null)
  189.             return true;
  190.     } else {
  191.         for (int i = tab.length ; i-- > 0 ;)
  192.         for (Entry e = tab[i] ; e != null ; e = e.next)
  193.             if (value.equals(e.value))
  194.             return true;
  195.     }
  196.  
  197.     return false;
  198.     }
  199.  
  200.     /**
  201.      * Returns true if this HashMap contains a mapping for the specified key.
  202.      * 
  203.      * @param key key whose presence in this Map is to be tested.
  204.      */
  205.     public boolean containsKey(Object key) {
  206.     Entry tab[] = table;
  207.         if (key != null) {
  208.             int hash = key.hashCode();
  209.             int index = (hash & 0x7FFFFFFF) % tab.length;
  210.             for (Entry e = tab[index]; e != null; e = e.next)
  211.                 if (e.hash==hash && e.key.equals(key))
  212.                     return true;
  213.         } else {
  214.             for (Entry e = tab[0]; e != null; e = e.next)
  215.                 if (e.key==null)
  216.                     return true;
  217.         }
  218.  
  219.     return false;
  220.     }
  221.  
  222.     /**
  223.      * Returns the value to which this HashMap maps the specified key.
  224.      * Returns null if the HashMap contains no mapping for this key.  A return
  225.      * value of null does not <em>necessarily</em> indicate that the HashMap
  226.      * contains no mapping for the key; it's also possible that the HashMap
  227.      * explicitly maps the key to null.  The containsKey operation may be
  228.      * used to distinguish these two cases.
  229.      *
  230.      * @param key key whose associated value is to be returned.
  231.      */
  232.     public Object get(Object key) {
  233.     Entry tab[] = table;
  234.  
  235.         if (key != null) {
  236.             int hash = key.hashCode();
  237.             int index = (hash & 0x7FFFFFFF) % tab.length;
  238.             for (Entry e = tab[index]; e != null; e = e.next)
  239.                 if ((e.hash == hash) && e.key.equals(key))
  240.                     return e.value;
  241.     } else {
  242.             for (Entry e = tab[0]; e != null; e = e.next)
  243.                 if (e.key==null)
  244.                     return e.value;
  245.         }
  246.  
  247.     return null;
  248.     }
  249.  
  250.     /**
  251.      * Rehashes the contents of the HashMap into a HashMap with a 
  252.      * larger capacity. This method is called automatically when the 
  253.      * number of keys in the HashMap exceeds this HashMap's capacity 
  254.      * and load factor. 
  255.      */
  256.     private void rehash() {
  257.     int oldCapacity = table.length;
  258.     Entry oldMap[] = table;
  259.  
  260.     int newCapacity = oldCapacity * 2 + 1;
  261.     Entry newMap[] = new Entry[newCapacity];
  262.  
  263.     modCount++;
  264.     threshold = (int)(newCapacity * loadFactor);
  265.     table = newMap;
  266.  
  267.     for (int i = oldCapacity ; i-- > 0 ;) {
  268.         for (Entry old = oldMap[i] ; old != null ; ) {
  269.         Entry e = old;
  270.         old = old.next;
  271.  
  272.         int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  273.         e.next = newMap[index];
  274.         newMap[index] = e;
  275.         }
  276.     }
  277.     }
  278.  
  279.     /**
  280.      * Associates the specified value with the specified key in this HashMap.
  281.      * If the HashMap previously contained a mapping for this key, the old
  282.      * value is replaced.
  283.      *
  284.      * @param key key with which the specified value is to be associated.
  285.      * @param value value to be associated with the specified key.
  286.      * @return previous value associated with specified key, or null if there
  287.      *           was no mapping for key.  A null return can also indicate that
  288.      *           the HashMap previously associated null with the specified key.
  289.      */
  290.     public Object put(Object key, Object value) {
  291.     // Makes sure the key is not already in the HashMap.
  292.     Entry tab[] = table;
  293.         int hash = 0;
  294.         int index = 0;
  295.  
  296.         if (key != null) {
  297.             hash = key.hashCode();
  298.             index = (hash & 0x7FFFFFFF) % tab.length;
  299.             for (Entry e = tab[index] ; e != null ; e = e.next) {
  300.                 if ((e.hash == hash) && e.key.equals(key)) {
  301.                     Object old = e.value;
  302.                     e.value = value;
  303.                     return old;
  304.                 }
  305.             }
  306.         } else {
  307.             for (Entry e = tab[0] ; e != null ; e = e.next) {
  308.                 if (e.key == null) {
  309.                     Object old = e.value;
  310.                     e.value = value;
  311.                     return old;
  312.                 }
  313.             }
  314.         }
  315.  
  316.     modCount++;
  317.     if (count >= threshold) {
  318.         // Rehash the table if the threshold is exceeded
  319.         rehash();
  320.  
  321.             tab = table;
  322.             index = (hash & 0x7FFFFFFF) % tab.length;
  323.     }
  324.  
  325.     // Creates the new entry.
  326.     Entry e = new Entry(hash, key, value, tab[index]);
  327.     tab[index] = e;
  328.     count++;
  329.     return null;
  330.     }
  331.  
  332.     /**
  333.      * Removes the mapping for this key from this HashMap if present.
  334.      *
  335.      * @param key key whose mapping is to be removed from the Map.
  336.      * @return previous value associated with specified key, or null if there
  337.      *           was no mapping for key.  A null return can also indicate that
  338.      *           the HashMap previously associated null with the specified key.
  339.      */
  340.     public Object remove(Object key) {
  341.     Entry tab[] = table;
  342.  
  343.         if (key != null) {
  344.             int hash = key.hashCode();
  345.             int index = (hash & 0x7FFFFFFF) % tab.length;
  346.  
  347.             for (Entry e = tab[index], prev = null; e != null;
  348.                  prev = e, e = e.next) {
  349.                 if ((e.hash == hash) && e.key.equals(key)) {
  350.                     modCount++;
  351.                     if (prev != null)
  352.                         prev.next = e.next;
  353.                     else
  354.                         tab[index] = e.next;
  355.  
  356.                     count--;
  357.                     Object oldValue = e.value;
  358.                     e.value = null;
  359.                     return oldValue;
  360.                 }
  361.             }
  362.         } else {
  363.             for (Entry e = tab[0], prev = null; e != null;
  364.                  prev = e, e = e.next) {
  365.                 if (e.key == null) {
  366.                     modCount++;
  367.                     if (prev != null)
  368.                         prev.next = e.next;
  369.                     else
  370.                         tab[0] = e.next;
  371.  
  372.                     count--;
  373.                     Object oldValue = e.value;
  374.                     e.value = null;
  375.                     return oldValue;
  376.                 }
  377.             }
  378.         }
  379.  
  380.     return null;
  381.     }
  382.  
  383.     /**
  384.      * Copies all of the mappings from the specified Map to this HashMap
  385.      * These mappings will replace any mappings that this HashMap had for any
  386.      * of the keys currently in the specified Map. 
  387.      *
  388.      * @param t Mappings to be stored in this Map.
  389.      */
  390.     public void putAll(Map t) {
  391.     Iterator i = t.entries().iterator();
  392.     while (i.hasNext()) {
  393.         Map.Entry e = (Map.Entry) i.next();
  394.         put(e.getKey(), e.getValue());
  395.     }
  396.     }
  397.  
  398.     /**
  399.      * Removes all mappings from this HashMap.
  400.      */
  401.     public void clear() {
  402.     Entry tab[] = table;
  403.     modCount++;
  404.     for (int index = tab.length; --index >= 0; )
  405.         tab[index] = null;
  406.     count = 0;
  407.     }
  408.  
  409.     /**
  410.      * Returns a shallow copy of this HashMap. The keys and values 
  411.      * themselves are not cloned. 
  412.      */
  413.     public Object clone() {
  414.     try { 
  415.         HashMap t = (HashMap)super.clone();
  416.         t.table = new Entry[table.length];
  417.         for (int i = table.length ; i-- > 0 ; ) {
  418.         t.table[i] = (table[i] != null) 
  419.             ? (Entry)table[i].clone() : null;
  420.         }
  421.         t.keySet = null;
  422.         t.entries = null;
  423.             t.values = null;
  424.         t.modCount = 0;
  425.         return t;
  426.     } catch (CloneNotSupportedException e) { 
  427.         // this shouldn't happen, since we are Cloneable
  428.         throw new InternalError();
  429.     }
  430.     }
  431.  
  432.     // Views
  433.  
  434.     private transient Set keySet = null;
  435.     private transient Set entries = null;
  436.     private transient Collection values = null;
  437.  
  438.     /**
  439.      * Returns a Set view of the keys contained in this HashMap.  The Set is
  440.      * backed by the HashMap, so changes to the HashMap are reflected in the
  441.      * Set, and vice-versa.  The Set supports element removal, which removes
  442.      * the corresponding mapping from the HashMap, via the Iterator.remove,
  443.      * Set.remove, removeAll retainAll, and clear operations.  It does not
  444.      * support the add or addAll operations.
  445.      */
  446.     public Set keySet() {
  447.     if (keySet == null) {
  448.         keySet = new AbstractSet() {
  449.         public Iterator iterator() {
  450.             return new HashIterator(KEYS);
  451.         }
  452.         public int size() {
  453.             return count;
  454.         }
  455.                 public boolean contains(Object o) {
  456.                     return containsKey(o);
  457.                 }
  458.         public boolean remove(Object o) {
  459.             return HashMap.this.remove(o) != null;
  460.         }
  461.         public void clear() {
  462.             HashMap.this.clear();
  463.         }
  464.         };
  465.     }
  466.     return keySet;
  467.     }
  468.  
  469.     /**
  470.      * Returns a Collection view of the values contained in this HashMap.
  471.      * The Collection is backed by the HashMap, so changes to the HashMap are
  472.      * reflected in the Collection, and vice-versa.  The Collection supports
  473.      * element removal, which removes the corresponding mapping from the
  474.      * HashMap, via the Iterator.remove, Collection.remove, removeAll,
  475.      * retainAll and clear operations.  It does not support the add or addAll
  476.      * operations.
  477.      */
  478.     public Collection values() {
  479.     if (values==null) {
  480.         values = new AbstractCollection() {
  481.                 public Iterator iterator() {
  482.                     return new HashIterator(VALUES);
  483.                 }
  484.                 public int size() {
  485.                     return count;
  486.                 }
  487.                 public boolean contains(Object o) {
  488.                     return containsValue(o);
  489.                 }
  490.                 public void clear() {
  491.                     HashMap.this.clear();
  492.                 }
  493.             };
  494.         }
  495.     return values;
  496.     }
  497.  
  498.     /**
  499.      * Returns a Collection view of the mappings contained in this HashMap.
  500.      * Each element in the returned collection is a Map.Entry.  The Collection
  501.      * is backed by the HashMap, so changes to the HashMap are reflected in the
  502.      * Collection, and vice-versa.  The Collection supports element removal,
  503.      * which removes the corresponding mapping from the HashMap, via the
  504.      * Iterator.remove, Collection.remove, removeAll, retainAll and clear
  505.      * operations.  It does not support the add or addAll operations.
  506.      *
  507.      * @see   Map.Entry
  508.      */
  509.     public Set entries() {
  510.     if (entries==null) {
  511.         entries = new AbstractSet() {
  512.                 public Iterator iterator() {
  513.                     return new HashIterator(ENTRIES);
  514.                 }
  515.  
  516.                 public boolean contains(Object o) {
  517.                     if (!(o instanceof Map.Entry))
  518.                         return false;
  519.                     Map.Entry entry = (Map.Entry)o;
  520.                     Object key = entry.getKey();
  521.                     Entry tab[] = table;
  522.                     int hash = (key==null ? 0 : key.hashCode());
  523.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  524.  
  525.                     for (Entry e = tab[index]; e != null; e = e.next)
  526.                         if (e.hash==hash && e.equals(entry))
  527.                             return true;
  528.                     return false;
  529.                 }
  530.  
  531.         public boolean remove(Object o) {
  532.                     if (!(o instanceof Map.Entry))
  533.                         return false;
  534.                     Map.Entry entry = (Map.Entry)o;
  535.                     Object key = entry.getKey();
  536.                     Entry tab[] = table;
  537.                     int hash = (key==null ? 0 : key.hashCode());
  538.                     int index = (hash & 0x7FFFFFFF) % tab.length;
  539.  
  540.                     for (Entry e = tab[index], prev = null; e != null;
  541.                          prev = e, e = e.next) {
  542.                         if (e.hash==hash && e.equals(entry)) {
  543.                             modCount++;
  544.                             if (prev != null)
  545.                                 prev.next = e.next;
  546.                             else
  547.                                 tab[index] = e.next;
  548.  
  549.                             count--;
  550.                             e.value = null;
  551.                             return true;
  552.                         }
  553.                     }
  554.                     return false;
  555.                 }
  556.  
  557.                 public int size() {
  558.                     return count;
  559.                 }
  560.  
  561.                 public void clear() {
  562.                     HashMap.this.clear();
  563.                 }
  564.             };
  565.         }
  566.  
  567.     return entries;
  568.     }
  569.  
  570.     /**
  571.      * HashMap collision list entry.
  572.      */
  573.     private static class Entry implements Map.Entry {
  574.     int hash;
  575.     Object key;
  576.     Object value;
  577.     Entry next;
  578.  
  579.     Entry(int hash, Object key, Object value, Entry next) {
  580.         this.hash = hash;
  581.         this.key = key;
  582.         this.value = value;
  583.         this.next = next;
  584.     }
  585.  
  586.     protected Object clone() {
  587.         return new Entry(hash, key, value,
  588.                  (next==null ? null : (Entry)next.clone()));
  589.     }
  590.  
  591.     // Map.Entry Ops 
  592.  
  593.     public Object getKey() {
  594.         return key;
  595.     }
  596.  
  597.     public Object getValue() {
  598.         return value;
  599.     }
  600.  
  601.     public Object setValue(Object value) {
  602.         Object oldValue = this.value;
  603.         this.value = value;
  604.         return oldValue;
  605.     }
  606.  
  607.     public boolean equals(Object o) {
  608.         if (!(o instanceof Map.Entry))
  609.         return false;
  610.         Map.Entry e = (Map.Entry)o;
  611.  
  612.         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  613.            (value==null ? e.getValue()==null : value.equals(e.getValue()));
  614.     }
  615.  
  616.     public int hashCode() {
  617.         return hash ^ (value==null ? 0 : value.hashCode());
  618.     }
  619.  
  620.     public String toString() {
  621.         return key.toString()+"="+value.toString();
  622.     }
  623.     }
  624.  
  625.     // Types of Enumerations/Iterations
  626.     private static final int KEYS = 0;
  627.     private static final int VALUES = 1;
  628.     private static final int ENTRIES = 2;
  629.  
  630.     /**
  631.      * A HashMap enumerator class.  This class implements both the
  632.      * Enumeration and Iterator interfaces, but individual instances
  633.      * can be created with the Iterator methods disabled.  This is necessary
  634.      * to avoid unintentionally increasing the capabilities granted a user
  635.      * by passing an Enumeration.
  636.      */
  637.     private class HashIterator implements Iterator {
  638.     Entry[] table = HashMap.this.table;
  639.     int index = table.length;
  640.     Entry entry = null;
  641.     Entry lastReturned = null;
  642.     int type;
  643.  
  644.     /**
  645.      * The modCount value that the iterator believes that the backing
  646.      * List should have.  If this expectation is violated, the iterator
  647.      * has detected concurrent modification.
  648.      */
  649.     private int expectedModCount = modCount;
  650.  
  651.     HashIterator(int type) {
  652.         this.type = type;
  653.     }
  654.  
  655.     public boolean hasNext() {
  656.         if (entry != null) {
  657.         return true;
  658.         }
  659.         while (index-- > 0) {
  660.         if ((entry = table[index]) != null) {
  661.             return true;
  662.         }
  663.         }
  664.         return false;
  665.     }
  666.  
  667.     public Object next() {
  668.         if (modCount != expectedModCount)
  669.         throw new ConcurrentModificationException();
  670.         if (entry == null) {
  671.         while ((index-- > 0) && ((entry = table[index]) == null));
  672.         }
  673.         if (entry != null) {
  674.         Entry e = lastReturned = entry;
  675.         entry = e.next;
  676.         return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  677.         }
  678.         throw new NoSuchElementException();
  679.     }
  680.  
  681.     public void remove() {
  682.         if (lastReturned == null)
  683.         throw new IllegalStateException();
  684.         if (modCount != expectedModCount)
  685.         throw new ConcurrentModificationException();
  686.  
  687.         Entry[] tab = HashMap.this.table;
  688.         int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  689.  
  690.         for (Entry e = tab[index], prev = null; e != null;
  691.          prev = e, e = e.next) {
  692.         if (e == lastReturned) {
  693.             modCount++;
  694.             expectedModCount++;
  695.             if (prev == null)
  696.             tab[index] = e.next;
  697.             else
  698.             prev.next = e.next;
  699.             count--;
  700.             lastReturned = null;
  701.             return;
  702.         }
  703.         }
  704.         throw new ConcurrentModificationException();
  705.     }
  706.     }
  707.  
  708.     /**
  709.      * Save the state of the HashMap to a stream (i.e., serialize it).
  710.      */
  711.     private void writeObject(java.io.ObjectOutputStream s)
  712.         throws IOException
  713.     {
  714.     // Write out the threshold, loadfactor, and any hidden stuff
  715.     s.defaultWriteObject();
  716.  
  717.     // Write out number of buckets
  718.     s.writeInt(table.length);
  719.  
  720.     // Write out size (number of Mappings)
  721.     s.writeInt(count);
  722.  
  723.         // Write out keys and values (alternating)
  724.     for (int index = table.length-1; index >= 0; index--) {
  725.         Entry entry = table[index];
  726.  
  727.         while (entry != null) {
  728.         s.writeObject(entry.key);
  729.         s.writeObject(entry.value);
  730.         entry = entry.next;
  731.         }
  732.     }
  733.     }
  734.  
  735.     /**
  736.      * Reconstitute the HashMap from a stream (i.e., deserialize it).
  737.      */
  738.     private void readObject(java.io.ObjectInputStream s)
  739.          throws IOException, ClassNotFoundException
  740.     {
  741.     // Read in the threshold, loadfactor, and any hidden stuff
  742.     s.defaultReadObject();
  743.  
  744.     // Read in number of buckets and allocate the bucket array;
  745.     int numBuckets = s.readInt();
  746.     table = new Entry[numBuckets];
  747.  
  748.     // Read in size (number of Mappings)
  749.     int size = s.readInt();
  750.  
  751.     // Read the keys and values, and put the mappings in the HashMap
  752.     for (int i=0; i<size; i++) {
  753.         Object key = s.readObject();
  754.         Object value = s.readObject();
  755.         put(key, value);
  756.     }
  757.     }
  758.  
  759.     int capacity() {
  760.         return table.length;
  761.     }
  762.  
  763.     float loadFactor() {
  764.         return loadFactor;
  765.     }
  766. }
  767.