/[classpath]/classpath/java/util/Hashtable.java
ViewVC logotype

Diff of /classpath/java/util/Hashtable.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.15 by ericb, Sun Sep 16 05:52:04 2001 UTC revision 1.16 by ericb, Tue Sep 18 03:25:11 2001 UTC
# Line 37  import java.io.ObjectOutputStream; Line 37  import java.io.ObjectOutputStream;
37  // code.  // code.
38    
39  /**  /**
40   * a class which implements a Hashtable data structure   * A class which implements a hashtable data structure.
41   *   *
42   * This implementation of Hashtable uses a hash-bucket approach. That is:   * This implementation of Hashtable uses a hash-bucket approach. That is:
43   * linear probing and rehashing is avoided; instead, each hashed value maps   * linear probing and rehashing is avoided; instead, each hashed value maps
# Line 45  import java.io.ObjectOutputStream; Line 45  import java.io.ObjectOutputStream;
45   * Assuming a large enough table, low enough load factor, and / or well   * Assuming a large enough table, low enough load factor, and / or well
46   * implemented hashCode() methods, Hashtable should provide O(1)   * implemented hashCode() methods, Hashtable should provide O(1)
47   * insertion, deletion, and searching of keys.  Hashtable is O(n) in   * insertion, deletion, and searching of keys.  Hashtable is O(n) in
48   * the worst case for all of these (if all keys has to the same bucket).   * the worst case for all of these (if all keys hash to the same bucket).
49     * <p>
50   *   *
51   * This is a JDK-1.2 compliant implementation of Hashtable.  As such, it   * This is a JDK-1.2 compliant implementation of Hashtable.  As such, it
52   * belongs, partially, to the Collections framework (in that it implements   * belongs, partially, to the Collections framework (in that it implements
53   * Map).  For backwards compatibility, it inherits from the obsolete and   * Map).  For backwards compatibility, it inherits from the obsolete and
54   * utterly useless Dictionary class.   * utterly useless Dictionary class.
55     * <p>
56   *   *
57   * Being a hybrid of old and new, Hashtable has methods which provide redundant   * Being a hybrid of old and new, Hashtable has methods which provide redundant
58   * capability, but with subtle and even crucial differences.   * capability, but with subtle and even crucial differences.
# Line 58  import java.io.ObjectOutputStream; Line 60  import java.io.ObjectOutputStream;
60   * either an Iterator (which is the JDK-1.2 way of doing things) or with an   * either an Iterator (which is the JDK-1.2 way of doing things) or with an
61   * Enumeration.  The latter can end up in an undefined state if the Hashtable   * Enumeration.  The latter can end up in an undefined state if the Hashtable
62   * changes while the Enumeration is open.   * changes while the Enumeration is open.
63     * <p>
64   *   *
65   * Unlike HashMap, Hashtable does not accept `null' as a key value.   * Unlike HashMap, Hashtable does not accept `null' as a key value. Also,
66     * all accesses are synchronized: in a single thread environment, this is
67     * expensive, but in a multi-thread environment, this saves you the effort
68     * of extra synchronization.
69   *   *
70   * @author      Jon Zeppieri   * @author Jon Zeppieri
71   * @author      Warren Levy   * @author Warren Levy
72   * @author      Bryce McKinlay   * @author Bryce McKinlay
73   * @author      Eric Blake <ebb9@email.byu.edu>   * @author Eric Blake <ebb9@email.byu.edu>
74     * @see HashMap
75     * @see TreeMap
76     * @see IdentityHashMap
77     * @see LinkedHashMap
78     * @since 1.0
79   */   */
80  public class Hashtable extends Dictionary  public class Hashtable extends Dictionary
81    implements Map, Cloneable, Serializable    implements Map, Cloneable, Serializable
82  {  {
83    /** Default number of buckets. This is the value the JDK 1.3 uses. Some    /** Default number of buckets. This is the value the JDK 1.3 uses. Some
84      * early documentation specified this value as 101. That is incorrect. */      * early documentation specified this value as 101. That is incorrect.
85    private static final int DEFAULT_CAPACITY = 11;        */
86      private static final int DEFAULT_CAPACITY = 11;
87    /** The default load factor; this is explicitly specified by the spec. */    /** The default load factor; this is explicitly specified by the spec. */
88    private static final float DEFAULT_LOAD_FACTOR = 0.75f;    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
89    
90      /**
91       * Compatible with JDK 1.0+.
92       */
93    private static final long serialVersionUID = 1421746759512286392L;    private static final long serialVersionUID = 1421746759512286392L;
94    
95    /**    /**
96     * The rounded product of the capacity and the load factor; when the number     * The rounded product of the capacity and the load factor; when the number
97     * of elements exceeds the threshold, the Hashtable calls <pre>rehash()</pre>.     * of elements exceeds the threshold, the Hashtable calls
98       * <pre>rehash()</pre>.
99     * @serial     * @serial
100     */     */
101    int threshold;    int threshold;
# Line 87  public class Hashtable extends Dictionar Line 103  public class Hashtable extends Dictionar
103    /** Load factor of this Hashtable:  used in computing the threshold.    /** Load factor of this Hashtable:  used in computing the threshold.
104     * @serial     * @serial
105     */     */
106    float loadFactor = DEFAULT_LOAD_FACTOR;    float loadFactor;
107    
108    /**    /**
109     * Array containing the actual key-value mappings     * Array containing the actual key-value mappings
110     */     */
111    transient Entry[] buckets;    transient HashEntry[] buckets;
112    
113    /**    /**
114     * counts the number of modifications this Hashtable has undergone, used     * Counts the number of modifications this Hashtable has undergone, used
115     * by Iterators to know when to throw ConcurrentModificationExceptions.     * by Iterators to know when to throw ConcurrentModificationExceptions.
116     */     */
117    transient int modCount;    transient int modCount;
118    
119    /** the size of this Hashtable:  denotes the number of key-value pairs */    /** The size of this Hashtable:  denotes the number of key-value pairs */
120    transient int size;    transient int size;
121    
122    /**    /**
# Line 108  public class Hashtable extends Dictionar Line 124  public class Hashtable extends Dictionar
124     * pair. A Hashtable Entry is identical to a HashMap Entry, except that     * pair. A Hashtable Entry is identical to a HashMap Entry, except that
125     * `null' is not allowed for keys and values.     * `null' is not allowed for keys and values.
126     */     */
127    static class Entry extends BasicMapEntry    static class HashEntry extends BasicMapEntry
128    {    {
129      Entry next;      /** The next entry in the linked list */
130              HashEntry next;
131      Entry(Object key, Object value)  
132        /**
133         * Simple constructor.
134         * @param key the key, already guaranteed non-null
135         * @param value the value, already guaranteed non-null
136         */
137        HashEntry(Object key, Object value)
138      {      {
139        super(key, value);        super(key, value);
140      }      }
141    
142        /**
143         * Resets the value.
144         * @param newValue the new value
145         * @return the prior value
146         * @throws NullPointerException if <code>newVal</code> is null
147         */
148      public final Object setValue(Object newVal)      public final Object setValue(Object newVal)
149      {      {
150        if (newVal == null)        // Throw a NullPointerException with minimal bytecode
151          throw new NullPointerException();        newVal.getClass();
152        return super.setValue(newVal);        return super.setValue(newVal);
153      }      }
   
     Entry copy()  
     {  
       Entry e = new Entry(key, value);  
       if (next != null)  
         e.next = next.copy();  
       return e;  
     }  
154    }    }
155    
156    /**    /**
157     * construct a new Hashtable with the default capacity (11) and the default     * Construct a new Hashtable with the default capacity (11) and the default
158     * load factor (0.75).     * load factor (0.75).
159     */     */
160    public Hashtable()    public Hashtable()
# Line 143  public class Hashtable extends Dictionar Line 163  public class Hashtable extends Dictionar
163    }    }
164    
165    /**    /**
166     * construct a new Hashtable from the given Map     * Construct a new Hashtable from the given Map, with initial capacity
167       * the greater of the size of m or the default of 11.
168       * <p>
169     *     *
170     * every element in Map t will be put into this new Hashtable     * Every element in Map m will be put into this new Hashtable
171     *     *
172     * @param     t        a Map whose key / value pairs will be put into     * @param m a Map whose key / value pairs will be put into
173     *                     the new Hashtable.  <b>NOTE: key / value pairs     *          the new Hashtable.  <b>NOTE: key / value pairs
174     *                     are not cloned in this constructor</b>     *          are not cloned in this constructor</b>
175       * @throws NullPointerException if m is null, or if m contains a mapping
176       *         to or from `null'.
177       * @since 1.2
178     */     */
179    public Hashtable(Map m)    public Hashtable(Map m)
180    {    {
# Line 158  public class Hashtable extends Dictionar Line 183  public class Hashtable extends Dictionar
183    }    }
184    
185    /**    /**
186     * construct a new Hashtable with a specific inital capacity     * Construct a new Hashtable with a specific inital capacity and
187     *     * default load factor of 0.75.
    * @param   initialCapacity     the initial capacity of this Hashtable (>=0)  
188     *     *
189     * @throws   IllegalArgumentException    if (initialCapacity < 0)     * @param initialCapacity the initial capacity of this Hashtable (>=0)
190       * @throws IllegalArgumentException if (initialCapacity < 0)
191     */     */
192    public Hashtable(int initialCapacity) throws IllegalArgumentException    public Hashtable(int initialCapacity)
193    {    {
194      this(initialCapacity, DEFAULT_LOAD_FACTOR);      this(initialCapacity, DEFAULT_LOAD_FACTOR);
195    }    }
196    
197    /**    /**
198     * construct a new Hashtable with a specific inital capacity and load factor     * Construct a new Hashtable with a specific initial capacity and
199       * load factor.
200     *     *
201     * @param   initialCapacity  the initial capacity (>=0)     * @param initialCapacity the initial capacity (>=0)
202     * @param   loadFactor       the load factor     * @param loadFactor the load factor (>0, not NaN)
203     *     * @throws IllegalArgumentException if (initialCapacity < 0) ||
204     * @throws   IllegalArgumentException    if (initialCapacity < 0) ||     *                                     ! (initialLoadFactor > 0.0)
    *                                          (initialLoadFactor <= 0.0)  
205     */     */
206    public Hashtable(int initialCapacity, float loadFactor)    public Hashtable(int initialCapacity, float loadFactor)
     throws IllegalArgumentException  
207    {    {
208      if (initialCapacity < 0)      if (initialCapacity < 0)
209        throw new IllegalArgumentException("Illegal Initial Capacity: "        throw new IllegalArgumentException("Illegal Capacity: "
210                                           + initialCapacity);                                               + initialCapacity);
211      if (loadFactor <= 0)      if (! (loadFactor > 0)) // check for NaN too
212        throw new IllegalArgumentException("Illegal Load Factor: " + loadFactor);        throw new IllegalArgumentException("Illegal Load: " + loadFactor);
213        
214      if (initialCapacity == 0)      if (initialCapacity == 0)
215        initialCapacity = 1;            initialCapacity = 1;
216      buckets = new Entry[initialCapacity];      buckets = new HashEntry[initialCapacity];
217      this.loadFactor = loadFactor;      this.loadFactor = loadFactor;
218      this.threshold = (int) (initialCapacity * loadFactor);      this.threshold = (int) (initialCapacity * loadFactor);
219    }    }
220    
221    /** Returns the number of key-value mappings currently in this Map */    /**
222    public int size()     * Returns the number of key-value mappings currently in this hashtable.
223       * @return the size
224       */
225      public synchronized int size()
226    {    {
227      return size;      return size;
228    }    }
229    
230    /** returns true if there are no key-value mappings currently in this Map */    /**
231    public boolean isEmpty()     * Returns true if there are no key-value mappings currently in this table.
232       * @return <code>size() == 0</code>
233       */
234      public synchronized boolean isEmpty()
235    {    {
236      return size == 0;      return size == 0;
237    }    }
238    
239      /**
240       * Return an enumeration of the keys of this table.
241       * @return the keys
242       * @see #elements()
243       * @see #keySet()
244       */
245    public synchronized Enumeration keys()    public synchronized Enumeration keys()
246    {    {
247      return new Enumerator(Enumerator.KEYS);      return new Enumerator(Enumerator.KEYS);
248    }    }
249      
250      /**
251       * Return an enumeration of the values of this table.
252       * @return the values
253       * @see #keys()
254       * @see #values()
255       */
256    public synchronized Enumeration elements()    public synchronized Enumeration elements()
257    {    {
258      return new Enumerator(Enumerator.VALUES);      return new Enumerator(Enumerator.VALUES);
259    }    }
260    
261    /**    /**
262     * returns true if this Hashtable contains a value <pre>o</pre>,     * Returns true if this Hashtable contains a value <pre>o</pre>,
263     * such that <pre>o.equals(value)</pre>.     * such that <pre>o.equals(value)</pre>.  This is the same as
264       * <code>containsValue()</code>, and is O(n).
265       * <p>
266     *     *
267     * Note: this is one of the <i>old</i> Hashtable methods which does     * Note: this is one of the <i>old</i> Hashtable methods which does
268     * not like null values; it throws NullPointerException if the     * not like null values; it throws NullPointerException if the
269     * supplied parameter is null.     * supplied parameter is null.
270     *     *
271     * @param     value        the value to search for in this Hashtable     * @param value the value to search for in this Hashtable
272     *     * @return true if at least one key maps to the value
273     * @throws NullPointerException if <pre>value</pre> is null     * @throws NullPointerException if <pre>value</pre> is null
274       * @see #containsValue(Object)
275       * @see #containsKey(Object)
276     */     */
277    public synchronized boolean contains(Object value)    public synchronized boolean contains(Object value)
278    {    {
279      // if Hashtable is empty, this method doesn't dereference      // Check if value is null with minimal bytecode, in case
280      // `value' anywhere, so check for it explicitly.      // Hashtable is empty.
281      if (value == null)      value.getClass();
282        throw new NullPointerException();  
283      for (int i = 0; i < buckets.length; i++)      for (int i = 0; i < buckets.length; i++)
284        {        {
285          Entry e = buckets[i];          HashEntry e = buckets[i];
286          while (e != null)          while (e != null)
287            {            {
288              if (value.equals(e.value))              if (value.equals(e.value))
289                return true;                return true;
290              e = e.next;              e = e.next;
291            }            }
292        }        }
293      return false;      return false;
294    }    }
295    
296    /**    /**
297     * returns true if this Hashtable contains a value <pre>o</pre>, such that     * Returns true if this Hashtable contains a value <pre>o</pre>, such that
298     * <pre>o.equals(value)</pre>.     * <pre>o.equals(value)</pre>. This is the new API for the old
299       * <code>contains()</code>.
300     *     *
301     * @param      value       the value to search for in this Hashtable     * @param value the value to search for in this Hashtable
302     *     * @return true if at least one key maps to the value
303     * @throws NullPointerException if <pre>value</pre> is null     * @throws NullPointerException if <pre>value</pre> is null
304       * @see #contains(Object)
305       * @see #containsKey(Object)
306       * @since 1.2
307     */     */
308    public boolean containsValue(Object value)    public boolean containsValue(Object value)
309    {    {
# Line 261  public class Hashtable extends Dictionar Line 311  public class Hashtable extends Dictionar
311    }    }
312    
313    /**    /**
314     * returns true if the supplied object equals (<pre>equals()</pre>) a key     * Returns true if the supplied object (<pre>equals()</pre>) a key
315     * in this Hashtable     * in this Hashtable
316     *     *
317     * @param       key        the key to search for in this Hashtable     * @param key the key to search for in this Hashtable
318       * @return true if the key is in the table
319       * @throws NullPointerException if key is null
320       * @see #containsValue(Object)
321     */     */
322    public synchronized boolean containsKey(Object key)    public synchronized boolean containsKey(Object key)
323    {    {
324      int idx = hash(key);      int idx = hash(key);
325      Entry e = buckets[idx];      HashEntry e = buckets[idx];
326      while (e != null)      while (e != null)
327        {        {
328          if (key.equals(e.key))          if (key.equals(e.key))
329            return true;            return true;
330          e = e.next;          e = e.next;
331        }        }
332      return false;      return false;
333    }    }
334    
335    /**    /**
336     * return the value in this Hashtable associated with the supplied key, or <pre>null</pre>     * Return the value in this Hashtable associated with the supplied key,
337     * if the key maps to nothing     * or <pre>null</pre> if the key maps to nothing
338     *     *
339     * @param     key      the key for which to fetch an associated value     * @param key the key for which to fetch an associated value
340       * @return what the key maps to, if present
341       * @throws NullPointerException if key is null
342       * @see #put(Object, Object)
343     */     */
344    public synchronized Object get(Object key)    public synchronized Object get(Object key)
345    {    {
346      int idx = hash(key);      int idx = hash(key);
347      Entry e = buckets[idx];      HashEntry e = buckets[idx];
348      while (e != null)      while (e != null)
349        {        {
350          if (key.equals(e.key))          if (key.equals(e.key))
351            return e.value;            return e.value;
352          e = e.next;          e = e.next;
353        }        }
354      return null;      return null;
355    }    }
356    
357    /**    /**
358     * puts the supplied value into the Map, mapped by the supplied key     * Puts the supplied value into the Map, mapped by the supplied key.
359       * Neither parameter may be null.  The value may be retrieved by any
360       * object which <code>equals()</code> this key.
361     *     *
362     * @param       key        the key used to locate the value     * @param key the key used to locate the value
363     * @param       value      the value to be stored in the table     * @param value the value to be stored in the table
364       * @return the prior mapping of the key, or null if there was none
365       * @throws NullPointerException if key or value is null
366       * @see #get(Object)
367       * @see Object#equals(Object)
368     */     */
369    public synchronized Object put(Object key, Object value)    public synchronized Object put(Object key, Object value)
370    {    {
371      modCount++;      modCount++;
372      int idx = hash(key);      int idx = hash(key);
373      Entry e = buckets[idx];      HashEntry e = buckets[idx];
374        
375      // Hashtable does not accept null values. This method doesn't dereference      // Check if value is null with minimal bytecode, since
376      // `value' anywhere, so check for it explicitly.      // Hashtable does not accept null values.
377      if (value == null)      value.getClass();
       throw new NullPointerException();  
378    
379      while (e != null)      while (e != null)
380        {        {
381          if (key.equals(e.key))          if (key.equals(e.key))
382            {            {
383              Object r = e.value;              Object r = e.value;
384              e.value = value;              e.value = value;
385              return r;              return r;
386            }            }
387          else          else
388            {            {
389              e = e.next;              e = e.next;
390            }            }
391        }        }
392        
393      // At this point, we know we need to add a new entry.      // At this point, we know we need to add a new entry.
394      if (++size > threshold)      if (++size > threshold)
395        {        {
396          rehash();          rehash();
397          // Need a new hash value to suit the bigger table.          // Need a new hash value to suit the bigger table.
398          idx = hash(key);          idx = hash(key);
399        }        }
400    
401      e = new Entry(key, value);      e = new HashEntry(key, value);
402        
403      e.next = buckets[idx];      e.next = buckets[idx];
404      buckets[idx] = e;      buckets[idx] = e;
405        
406      return null;      return null;
407    }    }
408    
409    /**    /**
410     * removes from the table and returns the value which is mapped by the     * Removes from the table and returns the value which is mapped by the
411     * supplied key; if the key maps to nothing, then the table remains     * supplied key. If the key maps to nothing, then the table remains
412     * unchanged, and <pre>null</pre> is returned     * unchanged, and <pre>null</pre> is returned.
413     *     *
414     * @param    key     the key used to locate the value to remove     * @param key the key used to locate the value to remove
415       * @return whatever the key mapped to, if present
416       * @throws NullPointerException if key is null
417     */     */
418    public synchronized Object remove(Object key)    public synchronized Object remove(Object key)
419    {    {
420      modCount++;      modCount++;
421      int idx = hash(key);      int idx = hash(key);
422      Entry e = buckets[idx];      HashEntry e = buckets[idx];
423      Entry last = null;      HashEntry last = null;
424    
425      while (e != null)      while (e != null)
426        {        {
427          if (key.equals(e.key))          if (key.equals(e.key))
428            {            {
429              if (last == null)              if (last == null)
430                buckets[idx] = e.next;                buckets[idx] = e.next;
431              else              else
432                last.next = e.next;                last.next = e.next;
433              size--;              size--;
434              return e.value;              return e.value;
435            }            }
436          last = e;          last = e;
437          e = e.next;          e = e.next;
438        }        }
439      return null;      return null;
440    }    }
441    
442      /**
443       * Copies all elements of the given map into this hashtable.  However, no
444       * mapping can contain null as key or value.  If this table already has
445       * a mapping for a key, the new mapping replaces the current one.
446       *
447       * @param m the map to be hashed into this
448       * @throws NullPointerException if m is null, or contains null keys or values
449       */
450    public synchronized void putAll(Map m)    public synchronized void putAll(Map m)
451    {    {
452      int msize = m.size();      int msize = m.size();
453      Iterator itr = m.entrySet().iterator();      Iterator itr = m.entrySet().iterator();
454        
455      for (int i=0; i < msize; i++)      for (int i = 0; i < msize; i++)
456        {        {
457          Map.Entry e = (Map.Entry) itr.next();          Entry e = (Entry) itr.next();
458          // Optimize in case the Entry is one of our own.          // Optimize in case the Entry is one of our own.
459          if (e instanceof BasicMapEntry)          if (e instanceof BasicMapEntry)
460            {            {
461              BasicMapEntry entry = (BasicMapEntry) e;              BasicMapEntry entry = (BasicMapEntry) e;
462              put(entry.key, entry.value);              put(entry.key, entry.value);
463            }            }
464          else          else
465            {            {
466              put(e.getKey(), e.getValue());              put(e.getKey(), e.getValue());
467            }            }
468        }        }
469    }    }
470      
471      /**
472       * Clears the hashtable so it has no keys.  This is O(1).
473       */
474    public synchronized void clear()    public synchronized void clear()
475    {    {
476      modCount++;      modCount++;
477      buckets = new Entry[buckets.length];      buckets = new HashEntry[buckets.length];
478      size = 0;      size = 0;
479    }    }
480    
481    /**    /**
482     * returns a shallow clone of this Hashtable (i.e. the Map itself is cloned,     * Returns a shallow clone of this Hashtable. The Map itself is cloned,
483     * but its contents are not)     * but its contents are not.  This is O(n).
484       *
485       * @return the clone
486     */     */
487    public synchronized Object clone()    public synchronized Object clone()
488    {    {
# Line 418  public class Hashtable extends Dictionar Line 494  public class Hashtable extends Dictionar
494      catch (CloneNotSupportedException x)      catch (CloneNotSupportedException x)
495        {        {
496        }        }
497      copy.buckets = new Entry[buckets.length];      copy.buckets = new HashEntry[buckets.length];
498        
499      for (int i=0; i < buckets.length; i++)      for (int i = 0; i < buckets.length; i++)
500        {        {
501          Entry e = buckets[i];          HashEntry e = buckets[i];
502          if (e != null)          HashEntry last = null;
503            copy.buckets[i] = e.copy();  
504            while (e != null)
505              {
506                if (last == null)
507                  {
508                    last = new HashEntry(e.key, e.value);
509                    copy.buckets[i] = last;
510                  }
511                else
512                  {
513                    last.next = new HashEntry(e.key, e.value);
514                    last = last.next;
515                  }
516                e = e.next;
517              }
518        }        }
519      return copy;      return copy;
520    }    }
521      
522      /**
523       * Converts this Hashtable to a String, surrounded by braces (<pre>'{'</pre>
524       * and <pre>'}'</pre>), key/value pairs listed with an equals sign between,
525       * (<pre>'='</pre>), and pairs separated by comma and space
526       * (<pre>", "</pre>).
527       * <p>
528       *
529       * NOTE: if the <code>toString()</code> method of any key or value
530       * throws an exception, this will fail for the same reason.
531       *
532       * @return the string representation
533       */
534    public synchronized String toString()    public synchronized String toString()
535    {    {
536      Iterator entries = entrySet().iterator();      // Since we are already synchronized, and entrySet().iterator()
537        // would repeatedly re-lock/release the monitor, we directly use the
538        // unsynchronized HashIterator instead.
539        Iterator entries = new HashIterator(HashIterator.ENTRIES);
540      StringBuffer r = new StringBuffer("{");      StringBuffer r = new StringBuffer("{");
541      for (int pos = 0; pos < size; pos++)      for (int pos = 0; pos < size; pos++)
542        {        {
543          r.append(entries.next());          r.append(entries.next());
544          if (pos < size - 1)          if (pos < size - 1)
545            r.append(", ");            r.append(", ");
546        }        }
547      r.append("}");      r.append("}");
548      return r.toString();          return r.toString();
549    }    }
550    
551    /** returns a "set view" of this Hashtable's keys */    /**
552       * Returns a "set view" of this Hashtable's keys. The set is backed by
553       * the hashtable, so changes in one show up in the other.  The set supports
554       * element removal, but not element addition.  The set is properly
555       * synchronized on the original hashtable.  The set will throw a
556       * {@link NullPointerException} if null is passed to <code>contains</code>,
557       * <code>remove</code>, or related methods.
558       *
559       * @return a set view of the keys
560       * @see #values()
561       * @see #entrySet()
562       * @since 1.2
563       */
564    public Set keySet()    public Set keySet()
565    {    {
566      // Create a synchronized AbstractSet with custom implementations of those      // Create a synchronized AbstractSet with custom implementations of those
# Line 454  public class Hashtable extends Dictionar Line 571  public class Hashtable extends Dictionar
571        {        {
572          return size;          return size;
573        }        }
574          
575        public Iterator iterator()        public Iterator iterator()
576        {        {
577          return new HashIterator(HashIterator.KEYS);          return new HashIterator(HashIterator.KEYS);
578        }        }
579                
580        public void clear()        public void clear()
581        {        {
582          Hashtable.this.clear();          Hashtable.this.clear();
# Line 469  public class Hashtable extends Dictionar Line 586  public class Hashtable extends Dictionar
586        {        {
587          return Hashtable.this.containsKey(o);          return Hashtable.this.containsKey(o);
588        }        }
589          
590        public boolean remove(Object o)        public boolean remove(Object o)
591        {        {
592          return (Hashtable.this.remove(o) != null);          return (Hashtable.this.remove(o) != null);
593        }        }
594      };      };
595    
596      return Collections.synchronizedSet(r);      // We must specify the correct object to synchronize upon, hence the
597        // use of a non-public API
598        return new Collections.SynchronizedSet(this, r);
599    }    }
600      
601    /** Returns a "collection view" (or "bag view") of this Hashtable's values.  
602      */    /**
603       * Returns a "collection view" (or "bag view") of this Hashtable's values.
604       * The collection is backed by the hashtable, so changes in one show up
605       * in the other.  The collection supports element removal, but not element
606       * addition.  The collection is properly synchronized on the original
607       * hashtable.  The collection will throw a {@link NullPointerException}
608       * if null is passed to <code>contains</code> or related methods, but not
609       * if passed to <code>remove</code> or related methods.
610       *
611       * @return a bag view of the values
612       * @see #keySet()
613       * @see #entrySet()
614       * @since 1.2
615       */
616    public Collection values()    public Collection values()
617    {    {
618      // We don't bother overriding many of the optional methods, as doing so      // We don't bother overriding many of the optional methods, as doing so
# Line 491  public class Hashtable extends Dictionar Line 623  public class Hashtable extends Dictionar
623        {        {
624          return size;          return size;
625        }        }
626          
627        public Iterator iterator()        public Iterator iterator()
628        {        {
629          return new HashIterator(HashIterator.VALUES);          return new HashIterator(HashIterator.VALUES);
630        }        }
631          
632        public void clear()        public void clear()
633        {        {
634          Hashtable.this.clear();          Hashtable.this.clear();
635        }        }
636    
637          // Override this so that we check for null
638          public boolean contains(Object o)
639          {
640            return Hashtable.this.contains(o);
641          }
642      };      };
643        
644      return Collections.synchronizedCollection(r);      // We must specify the correct object to synchronize upon, hence the
645        // use of a non-public API
646        return new Collections.SynchronizedCollection(this, r);
647    }    }
648    
649    /** Returns a "set view" of this Hashtable's entries. */    /**
650       * Returns a "set view" of this Hashtable's entries. The set is backed by
651       * the hashtable, so changes in one show up in the other.  The set supports
652       * element removal, but not element addition.  The set is properly
653       * synchronized on the original hashtable.  The set will throw a
654       * {@link NullPointerException} if the Map.Entry passed to
655       * <code>contains</code>, <code>remove</code>, or related methods returns
656       * null for <code>getKey</code>, but not if the Map.Entry is null or
657       * returns null for <code>getValue</code>.
658       * <p>
659       *
660       * Note that the iterators for all three views, from keySet(), entrySet(),
661       * and values(), traverse the hashtable in the same sequence.
662       *
663       * @return a set view of the entries
664       * @see #keySet()
665       * @see #values()
666       * @see Map.Entry
667       * @since 1.2
668       */
669    public Set entrySet()    public Set entrySet()
670    {    {
671      // Create an AbstractSet with custom implementations of those methods that      // Create an AbstractSet with custom implementations of those methods that
# Line 517  public class Hashtable extends Dictionar Line 676  public class Hashtable extends Dictionar
676        {        {
677          return size;          return size;
678        }        }
679          
680        public Iterator iterator()        public Iterator iterator()
681        {        {
682          return new HashIterator(HashIterator.ENTRIES);          return new HashIterator(HashIterator.ENTRIES);
683        }        }
684                
685        public void clear()        public void clear()
686        {        {
687          Hashtable.this.clear();          Hashtable.this.clear();
# Line 530  public class Hashtable extends Dictionar Line 689  public class Hashtable extends Dictionar
689    
690        public boolean contains(Object o)        public boolean contains(Object o)
691        {        {
692          if (!(o instanceof Map.Entry))          if (!(o instanceof Entry))
693            return false;            return false;
694          Map.Entry me = (Map.Entry) o;          Entry me = (Entry) o;
695          Entry e = getEntry(me);          HashEntry e = getEntry(me);
696          return (e != null);          return (e != null);
697        }        }
698          
699        public boolean remove(Object o)        public boolean remove(Object o)
700        {        {
701          if (!(o instanceof Map.Entry))          if (!(o instanceof Entry))
702            return false;            return false;
703          Map.Entry me = (Map.Entry) o;          Entry me = (Entry) o;
704          Entry e = getEntry(me);          HashEntry e = getEntry(me);
705          if (e != null)          if (e != null)
706            {            {
707              Hashtable.this.remove(e.key);              Hashtable.this.remove(e.key);
708              return true;              return true;
709            }            }
710          return false;          return false;
711        }        }
712      };      };
713        
714      return Collections.synchronizedSet(r);      // We must specify the correct object to synchronize upon, hence the
715        // use of a non-public API
716        return new Collections.SynchronizedSet(this, r);
717    }    }
718      
719    /** returns true if this Hashtable equals the supplied Object <pre>o</pre>;    /**
720     * that is:     * Returns true if this Hashtable equals the supplied Object <pre>o</pre>.
721       * As specified by Map, this is:
722     * <pre>     * <pre>
723     * if (o instanceof Map)     * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
724     * and     * </pre>
725     * o.keySet().equals(keySet())     *
726     * and     * @param o the object to compare to
727     * for each key in o.keySet(), o.get(key).equals(get(key))     * @return true if o is an equal map
728     *</pre>     * @since 1.2
729     */     */
730    public boolean equals(Object o)    public boolean equals(Object o)
731    {    {
732        // no need to synchronize, entrySet().equals() does that
733      if (o == this)      if (o == this)
734        return true;        return true;
735      if (!(o instanceof Map))      if (!(o instanceof Map))
736        return false;        return false;
737    
738      Map m = (Map) o;      return entrySet().equals(((Map) o).entrySet());
     Set s = m.entrySet();  
     Iterator itr = entrySet().iterator();  
   
     if (m.size() != size)  
       return false;  
   
     for (int pos = 0; pos < size; pos++)  
       {  
         if (!s.contains(itr.next()))  
           return false;  
       }  
     return true;      
739    }    }
740      
741    /** a Map's hashCode is the sum of the hashCodes of all of its    /**
742        Map.Entry objects */     * Returns the hashCode for this Hashtable.  As specified by Map, this is
743    public int hashCode()     * the sum of the hashCodes of all of its Map.Entry objects
744       *
745       * @return the sum of the hashcodes of the entries
746       * @since 1.2
747       */
748      public synchronized int hashCode()
749    {    {
750        // Since we are already synchronized, and entrySet().iterator()
751        // would repeatedly re-lock/release the monitor, we directly use the
752        // unsynchronized HashIterator instead.
753        Iterator itr = new HashIterator(HashIterator.ENTRIES);
754      int hashcode = 0;      int hashcode = 0;
     Iterator itr = entrySet().iterator();  
755      for (int pos = 0; pos < size; pos++)      for (int pos = 0; pos < size; pos++)
756        {        {
757          hashcode += itr.next().hashCode();          hashcode += itr.next().hashCode();
758        }        }
759      return hashcode;        return hashcode;
760    }    }
761      
762    /** Return an index in the buckets array for `key' based on its hashCode() */    /**
763       * Helper method that returns an index in the buckets array for `key'
764       * based on its hashCode().
765       *
766       * @param key the key
767       * @return the bucket number
768       * @throws NullPointerException if key is null
769       */
770    private int hash(Object key)    private int hash(Object key)
771    {    {
772      return Math.abs(key.hashCode() % buckets.length);      return Math.abs(key.hashCode() % buckets.length);
773    }    }
774    
775    private Entry getEntry(Map.Entry me)    /**
776       * Helper method for entrySet(), which matches both key and value
777       * simultaneously.
778       *
779       * @param me the entry to match
780       * @return the matching entry, if found, or null
781       * @throws NullPointerException if me.getKey() returns null
782       * @see #entrySet()
783       */
784      private HashEntry getEntry(Entry me)
785    {    {
786        if (me == null)
787          return null;
788      int idx = hash(me.getKey());      int idx = hash(me.getKey());
789      Entry e = buckets[idx];      HashEntry e = buckets[idx];
790      while (e != null)      while (e != null)
791        {        {
792          if (e.equals(me))          if (e.equals(me))
793            return e;            return e;
794          e = e.next;          e = e.next;
795        }        }
796      return null;      return null;
797    }    }
798      
799    /**    /**
800     * increases the size of the Hashtable and rehashes all keys to new array     * Increases the size of the Hashtable and rehashes all keys to new array
801     * indices; this is called when the addition of a new value would cause     * indices; this is called when the addition of a new value would cause
802     * size() > threshold. Note that the existing Entry objects are reused in     * size() > threshold. Note that the existing Entry objects are reused in
803     * the new hash table.     * the new hash table.
804       * <p>
805       *
806       * This is not specified, but the new size is twice the current size plus
807       * one; this number is not always prime, unfortunately.
808     */     */
809    protected void rehash()    protected void rehash()
810    {    {
811      Entry[] oldBuckets = buckets;      HashEntry[] oldBuckets = buckets;
812        
813      int newcapacity = (buckets.length * 2) + 1;      int newcapacity = (buckets.length * 2) + 1;
814      threshold = (int) (newcapacity * loadFactor);      threshold = (int) (newcapacity * loadFactor);
815      buckets = new Entry[newcapacity];      buckets = new HashEntry[newcapacity];
816        
817      for (int i = 0; i < oldBuckets.length; i++)      for (int i = 0; i < oldBuckets.length; i++)
818        {        {
819          Entry e = oldBuckets[i];          HashEntry e = oldBuckets[i];
820          while (e != null)          while (e != null)
821            {            {
822              int idx = hash(e.key);              int idx = hash(e.key);
823              Entry dest = buckets[idx];              HashEntry dest = buckets[idx];
824    
825              if (dest != null)              if (dest != null)
826                {                {
827                  while (dest.next != null)                  while (dest.next != null)
828                    dest = dest.next;                    dest = dest.next;
829                  dest.next = e;                  dest.next = e;
830                }                }
831              else              else
832                {                {
833                  buckets[idx] = e;                  buckets[idx] = e;
834                }                }
835    
836              Entry next = e.next;              HashEntry next = e.next;
837              e.next = null;              e.next = null;
838              e = next;              e = next;
839            }            }
840        }        }
841    }    }
842    
843    /**    /**
844     * Serializes this object to the given stream.     * Serializes this object to the given stream.
845     * @serialdata the <i>capacity</i>(int) that is the length of the     *
846     * bucket array, the <i>size</i>(int) of the hash map are emitted     * @param s the stream to write to
847     * first.  They are followed by size entries, each consisting of     * @throws IOException if the underlying stream fails
848     * a key (Object) and a value (Object).     * @serialData the <i>capacity</i>(int) that is the length of the
849       *             bucket array, the <i>size</i>(int) of the hash map
850       *             are emitted first.  They are followed by size entries,
851       *             each consisting of a key (Object) and a value (Object).
852     */     */
853    private void writeObject(ObjectOutputStream s) throws IOException    private synchronized void writeObject(ObjectOutputStream s)
854        throws IOException
855    {    {
856      // the threshold and loadFactor fields      // Write the threshold and loadFactor fields.
857      s.defaultWriteObject();      s.defaultWriteObject();
858    
859      s.writeInt(buckets.length);      s.writeInt(buckets.length);
860      s.writeInt(size);      s.writeInt(size);
861      Iterator it = entrySet().iterator();      // Since we are already synchronized, and entrySet().iterator()
862        // would repeatedly re-lock/release the monitor, we directly use the
863        // unsynchronized HashIterator instead.
864        Iterator it = new HashIterator(HashIterator.ENTRIES);
865      while (it.hasNext())      while (it.hasNext())
866        {        {
867          Map.Entry entry = (Map.Entry) it.next();          HashEntry entry = (HashEntry) it.next();
868          s.writeObject(entry.getKey());          s.writeObject(entry.key);
869          s.writeObject(entry.getValue());          s.writeObject(entry.value);
870        }        }
871    }    }
872    
873    /**    /**
874     * Deserializes this object from the given stream.     * Deserializes this object from the given stream.
875     * @serialdata the <i>capacity</i>(int) that is the length of the     *
876     * bucket array, the <i>size</i>(int) of the hash map are emitted     * @param s the stream to read from
877     * first.  They are followed by size entries, each consisting of     * @throws ClassNotFoundException if the underlying stream fails
878     * a key (Object) and a value (Object).     * @throws IOException if the underlying stream fails
879       * @serialData the <i>capacity</i>(int) that is the length of the
880       *             bucket array, the <i>size</i>(int) of the hash map
881       *             are emitted first.  They are followed by size entries,
882       *             each consisting of a key (Object) and a value (Object).
883     */     */
884    private void readObject(ObjectInputStream s)    private void readObject(ObjectInputStream s)
885      throws IOException, ClassNotFoundException      throws IOException, ClassNotFoundException
886    {    {
887      // the threshold and loadFactor fields      // Read the threshold and loadFactor fields.
888      s.defaultReadObject();      s.defaultReadObject();
889    
890      int capacity = s.readInt();      int capacity = s.readInt();
891      int len = s.readInt();      int len = s.readInt();
892      size = 0;      size = 0;
893      modCount = 0;      modCount = 0;
894      buckets = new Entry[capacity];      buckets = new HashEntry[capacity];
895    
896      for (int i = 0; i < len; i++)      for (int i = 0; i < len; i++)
897        {        {
898          Object key = s.readObject();          Object key = s.readObject();
899          Object value = s.readObject();          Object value = s.readObject();
900          put(key, value);          put(key, value);
901        }        }
902    }    }
903    
904    /**    /**
905     * a class which implements the Iterator interface and is used for     * A class which implements the Iterator interface and is used for
906     * iterating over Hashtables;     * iterating over Hashtables.
907     * this implementation is parameterized to give a sequential view of     * This implementation is parameterized to give a sequential view of
908     * keys, values, or entries; it also allows the removal of elements,     * keys, values, or entries; it also allows the removal of elements,
909     * as per the Javasoft spec.     * as per the Javasoft spec.  Note that it is not synchronized; this is
910       * a performance enhancer since it is never exposed externally and is
911       * only used within synchronized blocks above.
912     *     *
913     * @author       Jon Zeppieri     * @author Jon Zeppieri
914     */     */
915    class HashIterator implements Iterator    class HashIterator implements Iterator
916    {    {
917      static final int KEYS = 0,      /** "enum" of iterator types. */
918                       VALUES = 1,      static final int KEYS = 0;
919                       ENTRIES = 2;      /** "enum" of iterator types. */
920                            static final int VALUES = 1;
921      // The type of this Iterator: KEYS, VALUES, or ENTRIES.      /** "enum" of iterator types. */
922        static final int ENTRIES = 2;
923    
924        /**
925         * The type of this Iterator: {@link KEYS}, {@link VALUES},
926         * or {@link ENTRIES}.
927         */
928      int type;      int type;
929      // The number of modifications to the backing Hashtable that we know about.      /**
930         * The number of modifications to the backing Hashtable that we know about.
931         */
932      int knownMod;      int knownMod;
933      // The total number of elements returned by next(). Used to determine if      /**
934      // there are more elements remaining.       * The total number of elements returned by next(). Used to determine if
935         * there are more elements remaining.
936         */
937      int count;      int count;
938      // Current index in the physical hash table.      /** Current index in the physical hash table. */
939      int idx;      int idx;
940      // The last Entry returned by a next() call.      /** The last Entry returned by a next() call. */
941      Entry last;      HashEntry last;
942      // The next entry that should be returned by next(). It is set to something      /**
943      // if we're iterating through a bucket that contains multiple linked       * The next entry that should be returned by next(). It is set to something
944      // entries. It is null if next() needs to find a new bucket.       * if we're iterating through a bucket that contains multiple linked
945      Entry next;       * entries. It is null if next() needs to find a new bucket.
946         */
947        HashEntry next;
948    
949      /* Construct a new HashIterator with the supplied type:      /**
950         KEYS, VALUES, or ENTRIES */       * Construct a new HashIterator with the supplied type.
951         * @param type {@link KEYS}, {@link VALUES}, or {@link ENTRIES}
952         */
953      HashIterator(int type)      HashIterator(int type)
954      {      {
955        this.type = type;        this.type = type;
# Line 750  public class Hashtable extends Dictionar Line 958  public class Hashtable extends Dictionar
958        idx = buckets.length;        idx = buckets.length;
959      }      }
960    
961      /** returns true if the Iterator has more elements */      /**
962         * Returns true if the Iterator has more elements.
963         * @return true if there are more elements
964         * @throws ConcurrentModificationException if the hashtable was modified
965         */
966      public boolean hasNext()      public boolean hasNext()
967      {      {
968        if (knownMod != Hashtable.this.modCount)        if (knownMod != Hashtable.this.modCount)
969          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
970        return count < size;        return count < size;
971      }      }
972    
973      /** Returns the next element in the Iterator's sequential view. */      /**
974         * Returns the next element in the Iterator's sequential view.
975         * @return the next element
976         * @throws ConcurrentModificationException if the hashtable was modified
977         * @throws NoSuchElementException if there is none
978         */
979      public Object next()      public Object next()
980      {      {
981        if (knownMod != Hashtable.this.modCount)        if (knownMod != Hashtable.this.modCount)
982          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
983        if (count == size)        if (count == size)
984          throw new NoSuchElementException();          throw new NoSuchElementException();
985        count++;        count++;
986        Entry e = null;        HashEntry e = null;
987        if (next != null)        if (next != null)
988          e = next;          e = next;
989    
990        while (e == null)        while (e == null)
991          {          {
992            e = buckets[--idx];            e = buckets[--idx];
993          }          }
994    
995        next = e.next;        next = e.next;
996        last = e;        last = e;
# Line 787  public class Hashtable extends Dictionar Line 1004  public class Hashtable extends Dictionar
1004      /**      /**
1005       * Removes from the backing Hashtable the last element which was fetched       * Removes from the backing Hashtable the last element which was fetched
1006       * with the <pre>next()</pre> method.       * with the <pre>next()</pre> method.
1007         * @throws ConcurrentModificationException if the hashtable was modified
1008         * @throws IllegalStateException if called when there is no last element
1009       */       */
1010      public void remove()      public void remove()
1011      {      {
1012        if (knownMod != Hashtable.this.modCount)        if (knownMod != Hashtable.this.modCount)
1013          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
1014        if (last == null)        if (last == null)
1015          {          throw new IllegalStateException();
1016            throw new IllegalStateException();  
1017          }        Hashtable.this.remove(last.key);
1018        else        knownMod++;
1019          {        count--;
1020            Hashtable.this.remove(last.key);        last = null;
           knownMod++;  
           count--;  
           last = null;  
         }  
1021      }      }
1022    }    }
1023    
# Line 819  public class Hashtable extends Dictionar Line 1034  public class Hashtable extends Dictionar
1034     * the "Java Class Libraries" book infers that modifications to the     * the "Java Class Libraries" book infers that modifications to the
1035     * hashtable during enumeration causes indeterminate results.  Don't do it!     * hashtable during enumeration causes indeterminate results.  Don't do it!
1036     *     *
1037     * @author       Jon Zeppieri     * @author Jon Zeppieri
1038     */     */
1039    class Enumerator implements Enumeration    class Enumerator implements Enumeration
1040    {    {
1041        /** "enum" of iterator types. */
1042      static final int KEYS = 0;      static final int KEYS = 0;
1043        /** "enum" of iterator types. */
1044      static final int VALUES = 1;      static final int VALUES = 1;
1045        
1046        /**
1047         * The type of this Iterator: {@link KEYS} or {@link VALUES}.
1048         */
1049      int type;      int type;
1050      // current index in the physical hash table.      /** Current index in the physical hash table. */
1051      int idx;      int idx;
1052      // the last Entry returned by nextEntry().      /** The last Entry returned by nextEntry(). */
1053      Entry last;      HashEntry last;
1054      // Entry which will be returned by the next nextElement() call.      /** Entry which will be returned by the next nextElement() call. */
1055      Entry next;      HashEntry next;
1056        
1057        /**
1058         * Construct the enumeration.
1059         * @param type either {@link KEYS} or {@link VALUES}.
1060         */
1061      Enumerator(int type)      Enumerator(int type)
1062      {      {
1063        this.type = type;        this.type = type;
1064        this.idx = buckets.length;        this.idx = buckets.length;
1065      }      }
1066        
1067      private Entry nextEntry()      /**
1068         * Helper method to find the next entry.
1069         * @return the next entry, or null
1070         */
1071        private HashEntry nextEntry()
1072      {      {
1073        Entry e = null;        HashEntry e = null;
1074    
1075        if (last != null)        if (last != null)
1076          e = last.next;          e = last.next;
1077    
1078        while (e == null && idx > 0)        while (e == null && idx > 0)
1079          {          {
1080            e = buckets[--idx];            e = buckets[--idx];
1081          }          }
1082        last = e;        last = e;
1083        return e;        return e;
1084      }      }
1085    
1086        /**
1087         * Checks whether more elements remain in the enumeration.
1088         * @return true if nextElement() will not fail.
1089         */
1090      public boolean hasMoreElements()      public boolean hasMoreElements()
1091      {      {
1092        if (next != null)        if (next != null)
# Line 863  public class Hashtable extends Dictionar Line 1095  public class Hashtable extends Dictionar
1095        return (next != null);        return (next != null);
1096      }      }
1097    
1098        /**
1099         * Returns the next element.
1100         * @return the next element
1101         * @throws NoSuchElementException if there is none.
1102         */
1103      public Object nextElement()      public Object nextElement()
1104      {      {
1105        Entry e = null;        HashEntry e;
1106        if (next != null)        if (next != null)
1107          {          {
1108            e = next;            e = next;
1109            next = null;            next = null;
1110          }          }
1111        else        else
1112          e = nextEntry();          e = nextEntry();
1113        if (e == null)        if (e == null)

Legend:
Removed from v.1.15  
changed lines
  Added in v.1.16

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26