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

Diff of /classpath/java/util/HashMap.java

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

revision 1.14 by ericb, Tue Sep 18 03:25:11 2001 UTC revision 1.15 by ericb, Thu Sep 20 23:38:12 2001 UTC
# Line 37  import java.io.ObjectOutputStream; Line 37  import java.io.ObjectOutputStream;
37  // a bug in here, chances are you should make a similar change to the Hashtable  // a bug in here, chances are you should make a similar change to the Hashtable
38  // code.  // code.
39    
40    // NOTE: This implementation has some nasty coding style in order to
41    // support LinkedHashMap, which extends this.
42    
43  /**  /**
44   * This class provides a hashtable-backed implementation of the   * This class provides a hashtable-backed implementation of the
45   * Map interface.     * Map interface.
46     * <p>
47   *   *
48   * It uses a hash-bucket approach; that is, hash   * It uses a hash-bucket approach; that is, hash collisions are handled
49   * collisions are handled by linking the new node off of the   * by linking the new node off of the pre-existing node (or list of
50   * pre-existing node (or list of nodes).  In this manner, techniques   * nodes).  In this manner, techniques such as linear probing (which
51   * such as linear probing (which can cause primary clustering) and   * can cause primary clustering) and rehashing (which does not fit very
52   * rehashing (which does not fit very well with Java's method of   * well with Java's method of precomputing hash codes) are avoided.
53   * precomputing hash codes) are avoided.     * <p>
54   *   *
55   * Under ideal circumstances (no collisions), HashMap offers O(1)   * Under ideal circumstances (no collisions), HashMap offers O(1)
56   * performance on most operations (<pre>containsValue()</pre> is,   * performance on most operations (<pre>containsValue()</pre> is,
57   * of course, O(n)).  In the worst case (all keys map to the same   * of course, O(n)).  In the worst case (all keys map to the same
58   * hash code -- very unlikely), most operations are O(n).   * hash code -- very unlikely), most operations are O(n).
59     * <p>
60   *   *
61   * HashMap is part of the JDK1.2 Collections API.  It differs from   * HashMap is part of the JDK1.2 Collections API.  It differs from
62   * Hashtable in that it accepts the null key and null values, and it   * Hashtable in that it accepts the null key and null values, and it
63   * does not support "Enumeration views."   * does not support "Enumeration views."
64     * <p>
65     *
66     * The iterators are <i>fail-fast</i>, meaning that any structural
67     * modification, except for <code>remove()</code> called on the iterator
68     * itself, cause the iterator to throw a
69     * <code>ConcurrentModificationException</code> rather than exhibit
70     * non-deterministic behavior.
71   *   *
72   * @author         Jon Zeppieri   * @author Jon Zeppieri
73   * @author         Jochen Hoenicke   * @author Jochen Hoenicke
74   * @author         Bryce McKinlay   * @author Bryce McKinlay
75   * @author         Eric Blake <ebb9@email.byu.edu>   * @author Eric Blake <ebb9@email.byu.edu>
76     * @see Object#hashCode()
77     * @see Collection
78     * @see Map
79     * @see TreeMap
80     * @see LinkedHashMap
81     * @see IdentityHashMap
82     * @see Hashtable
83     * @since 1.2
84   */   */
85  public class HashMap extends AbstractMap  public class HashMap extends AbstractMap
86    implements Map, Cloneable, Serializable    implements Map, Cloneable, Serializable
87  {  {
88    /** Default number of buckets. This is the value the JDK 1.3 uses. Some    /**
89      * early documentation specified this value as 101. That is incorrect. */     * Default number of buckets. This is the value the JDK 1.3 uses. Some
90    private static final int DEFAULT_CAPACITY = 11;       * early documentation specified this value as 101. That is incorrect.
91    /** The default load factor; this is explicitly specified by the spec. */     */
92    private static final float DEFAULT_LOAD_FACTOR = 0.75f;    static final int DEFAULT_CAPACITY = 11;
93    
94      /**
95       * The default load factor; this is explicitly specified by the spec.
96       */
97      static final float DEFAULT_LOAD_FACTOR = 0.75f;
98    
99      /** "enum" of iterator types. */
100      static final int KEYS = 0,
101                       VALUES = 1,
102                       ENTRIES = 2;
103    
104      /**
105       * Compatible with JDK 1.2.
106       */
107    private static final long serialVersionUID = 362498820763181265L;    private static final long serialVersionUID = 362498820763181265L;
108    
109    /**    /**
110     * 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
111     * of elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>.     * of elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>.
112     * @serial     * @serial
113     */     */
114    int threshold;    int threshold;
115    
116    /** Load factor of this HashMap:  used in computing the threshold.    /**
117       * Load factor of this HashMap:  used in computing the threshold.
118     * @serial     * @serial
119     */     */
120    float loadFactor = DEFAULT_LOAD_FACTOR;    final float loadFactor;
121    
122    /**    /**
123     * Array containing the actual key-value mappings     * Array containing the actual key-value mappings.
124     */     */
125    transient Entry[] buckets;    transient HashEntry[] buckets;
126    
127    /**    /**
128     * counts the number of modifications this HashMap has undergone, used     * Counts the number of modifications this HashMap has undergone, used
129     * by Iterators to know when to throw ConcurrentModificationExceptions.     * by Iterators to know when to throw ConcurrentModificationExceptions.
130     */     */
131    transient int modCount;    transient int modCount;
132    
133    /** the size of this HashMap:  denotes the number of key-value pairs */    /**
134       * The size of this HashMap:  denotes the number of key-value pairs.
135       */
136    transient int size;    transient int size;
137    
138    /**    /**
139     * Class to represent an entry in the hash table. Holds a single key-value     * Class to represent an entry in the hash table. Holds a single key-value
140     * pair.     * pair.  This is extended again in LinkedHashMap.  See {@link clone()}
141       * for why this must be Cloneable.
142     */     */
143    static class Entry extends BasicMapEntry    static class HashEntry extends BasicMapEntry implements Cloneable
144    {    {
145      Entry next;      /** The next entry in the linked list. */
146            HashEntry next;
147      Entry(Object key, Object value)  
148        /**
149         * Simple constructor.
150         * @param key the key
151         * @param value the value
152         */
153        HashEntry(Object key, Object value)
154      {      {
155        super(key, value);        super(key, value);
156      }      }
157    
158        /**
159         * Called when this entry is removed from the map. This version simply
160         * returns the value, but in LinkedHashMap, it must also do bookkeeping.
161         * @return the value of this key as it is removed
162         */
163        Object cleanup()
164        {
165          return value;
166        }
167    
168        /**
169         * Clone this Entry.
170         * @return the cloned object
171         */
172        protected Object clone()
173        {
174          try
175            {
176              return super.clone();
177            }
178          catch (CloneNotSupportedException e)
179            {
180              // This is impossible.
181              return null;
182            }
183        }
184    }    }
185    
186    /**    /**
187     * construct a new HashMap with the default capacity (11) and the default     * Construct a new HashMap with the default capacity (11) and the default
188     * load factor (0.75).     * load factor (0.75).
189     */     */
190    public HashMap()    public HashMap()
# Line 123  public class HashMap extends AbstractMap Line 193  public class HashMap extends AbstractMap
193    }    }
194    
195    /**    /**
196     * construct a new HashMap from the given Map     * Construct a new HashMap from the given Map, with initial capacity
197     *     * the greater of the size of <code>m</code> or the default of 11.
198     * every element in Map t will be put into this new HashMap     * <p>
199     *     *
200     * @param     t        a Map whose key / value pairs will be put into     * Every element in Map m will be put into this new HashMap.
201     *                     the new HashMap.  <b>NOTE: key / value pairs     *
202     *                     are not cloned in this constructor</b>     * @param m a Map whose key / value pairs will be put into
203       *          the new HashMap.  <b>NOTE: key / value pairs
204       *          are not cloned in this constructor.</b>
205       * @throws NullPointerException if m is null
206     */     */
207    public HashMap(Map m)    public HashMap(Map m)
208    {    {
209      this(Math.max(m.size() * 2, DEFAULT_CAPACITY));      this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
210      putAll(m);      putAll(m);
211    }    }
212    
213    /**    /**
214     * construct a new HashMap with a specific inital capacity     * Construct a new HashMap with a specific inital capacity and
215     *     * default load factor of 0.75.
    * @param   initialCapacity     the initial capacity of this HashMap (>=0)  
216     *     *
217     * @throws   IllegalArgumentException    if (initialCapacity < 0)     * @param initialCapacity the initial capacity of this HashMap (>=0)
218       * @throws IllegalArgumentException if (initialCapacity < 0)
219     */     */
220    public HashMap(int initialCapacity) throws IllegalArgumentException    public HashMap(int initialCapacity)
221    {    {
222      this(initialCapacity, DEFAULT_LOAD_FACTOR);      this(initialCapacity, DEFAULT_LOAD_FACTOR);
223    }    }
224    
225    /**    /**
226     * construct a new HashMap with a specific inital capacity and load factor     * Construct a new HashMap with a specific inital capacity and load factor.
227     *     *
228     * @param   initialCapacity  the initial capacity (>=0)     * @param initialCapacity the initial capacity (>=0)
229     * @param   loadFactor       the load factor     * @param loadFactor the load factor (>0, not NaN)
230     *     * @throws IllegalArgumentException if (initialCapacity < 0) ||
231     * @throws   IllegalArgumentException    if (initialCapacity < 0) ||     *                                     ! (loadFactor > 0.0)
    *                                          (loadFactor <= 0)  
232     */     */
233    public HashMap(int initialCapacity, float loadFactor)    public HashMap(int initialCapacity, float loadFactor)
     throws IllegalArgumentException  
234    {    {
235      if (initialCapacity < 0)      if (initialCapacity < 0)
236        throw new IllegalArgumentException("Illegal Initial Capacity: "        throw new IllegalArgumentException("Illegal Capacity: "
237                                           + initialCapacity);                                               + initialCapacity);
238      if (loadFactor <= 0)      if (! (loadFactor > 0)) // check for NaN too
239        throw new IllegalArgumentException("Illegal Load Factor: " + loadFactor);        throw new IllegalArgumentException("Illegal Load: " + loadFactor);
240    
241      if (initialCapacity == 0)      if (initialCapacity == 0)
242        initialCapacity = 1;        initialCapacity = 1;
243      buckets = new Entry[initialCapacity];      buckets = new HashEntry[initialCapacity];
244      this.loadFactor = loadFactor;      this.loadFactor = loadFactor;
245      this.threshold = (int) (initialCapacity * loadFactor);      threshold = (int) (initialCapacity * loadFactor);
246    }    }
247    
248    /** returns the number of kay-value mappings currently in this Map */    /**
249       * Returns the number of kay-value mappings currently in this Map
250       * @return the size
251       */
252    public int size()    public int size()
253    {    {
254      return size;      return size;
255    }    }
256    
257    /** returns true if there are no key-value mappings currently in this Map */    /**
258       * Returns true if there are no key-value mappings currently in this Map
259       * @return <code>size() == 0</code>
260       */
261    public boolean isEmpty()    public boolean isEmpty()
262    {    {
263      return size == 0;      return size == 0;
264    }    }
265    
266    /**    /**
267     * returns true if this HashMap contains a value <pre>o</pre>, such that     * Returns true if this HashMap contains a value <pre>o</pre>, such that
268     * <pre>o.equals(value)</pre>.     * <pre>o.equals(value)</pre>.
269     *     *
270     * @param      value       the value to search for in this Hashtable     * @param value the value to search for in this HashMap
271       * @return true if at least one key maps to the value
272     */     */
273    public boolean containsValue(Object value)    public boolean containsValue(Object value)
274    {    {
275      for (int i = 0; i < buckets.length; i++)      for (int i = buckets.length - 1; i >= 0; i--)
276        {        {
277          Entry e = buckets[i];          HashEntry e = buckets[i];
278          while (e != null)          while (e != null)
279            {            {
280              if (value == null ? e.value == null : value.equals(e.value))              if (value == null ? e.value == null : value.equals(e.value))
281                return true;                return true;
282              e = e.next;              e = e.next;
283            }            }
284        }        }
285      return false;      return false;
286    }    }
287    
288    /**    /**
289     * returns true if the supplied object equals (<pre>equals()</pre>) a key     * Returns true if the supplied object <pre>equals()</pre> a key
290     * in this HashMap     * in this HashMap.
291     *     *
292     * @param       key        the key to search for in this HashMap     * @param key the key to search for in this HashMap
293       * @return true if the key is in the table
294       * @see #containsValue(Object)
295     */     */
296    public boolean containsKey(Object key)    public boolean containsKey(Object key)
297    {    {
298      int idx = hash(key);      int idx = hash(key);
299      Entry e = buckets[idx];      HashEntry e = buckets[idx];
300      while (e != null)      while (e != null)
301        {        {
302          if (key == null ? e.key == null : key.equals(e.key))          if (key == null ? e.key == null : key.equals(e.key))
303            return true;            return true;
304          e = e.next;          e = e.next;
305        }        }
306      return false;      return false;
307    }    }
308    
309    /**    /**
310     * return the value in this Hashtable associated with the supplied key, or <pre>null</pre>     * Return the value in this HashMap associated with the supplied key,
311     * if the key maps to nothing     * or <pre>null</pre> if the key maps to nothing.  NOTE: Since the value
312       * could also be null, you must use containsKey to see if this key
313       * actually maps to something.
314     *     *
315     * @param     key      the key for which to fetch an associated value     * @param key the key for which to fetch an associated value
316       * @return what the key maps to, if present
317       * @see #put(Object, Object)
318       * @see #containsKey(Object)
319     */     */
320    public Object get(Object key)    public Object get(Object key)
321    {    {
322      int idx = hash(key);      int idx = hash(key);
323      Entry e = buckets[idx];      HashEntry e = buckets[idx];
324      while (e != null)      while (e != null)
325        {        {
326          if (key == null ? e.key == null : key.equals(e.key))          if (key == null ? e.key == null : key.equals(e.key))
327            return e.value;            return e.value;
328          e = e.next;          e = e.next;
329        }        }
330      return null;      return null;
331    }    }
332    
333    /**    /**
334     * puts the supplied value into the Map, mapped by the supplied key     * Puts the supplied value into the Map, mapped by the supplied key.
335       * The value may be retrieved by any object which <code>equals()</code>
336       * this key. NOTE: Since the prior value could also be null, you must
337       * first use containsKey if you want to see if you are replacing the
338       * key's mapping.
339     *     *
340     * @param       key        the HashMap key used to locate the value     * @param key the key used to locate the value
341     * @param       value      the value to be stored in the HashMap     * @param value the value to be stored in the HashMap
342       * @return the prior mapping of the key, or null if there was none
343       * @see #get(Object)
344       * @see Object#equals(Object)
345     */     */
346    public Object put(Object key, Object value)    public Object put(Object key, Object value)
347    {    {
348      modCount++;      modCount++;
349      int idx = hash(key);      int idx = hash(key);
350      Entry e = buckets[idx];      HashEntry e = buckets[idx];
351        
352      while (e != null)      while (e != null)
353        {        {
354          if (key == null ? e.key == null : key.equals(e.key))          if (key == null ? e.key == null : key.equals(e.key))
355            {            // Must use this method for necessary bookkeeping in LinkedHashMap.
356              Object r = e.value;            return e.setValue(value);
357              e.value = value;          else
358              return r;            e = e.next;
           }  
         else  
           {  
             e = e.next;  
           }  
359        }        }
360        
361      // At this point, we know we need to add a new entry.      // At this point, we know we need to add a new entry.
362      if (++size > threshold)      if (++size > threshold)
363        {        {
364          rehash();          rehash();
365          // Need a new hash value to suit the bigger table.          // Need a new hash value to suit the bigger table.
366          idx = hash(key);          idx = hash(key);
367        }        }
368    
369      e = new Entry(key, value);      // LinkedHashMap cannot override put(), hence this call.
370            addEntry(key, value, idx);
371        return null;
372      }
373    
374      /**
375       * Helper method for put, that creates and adds a new Entry.  This is
376       * overridden in LinkedHashMap for bookkeeping purposes.
377       *
378       * @param key the key of the new Entry
379       * @param value the value
380       * @param idx the index in buckets where the new Entry belongs
381       * @see #put(Object, Object)
382       */
383      void addEntry(Object key, Object value, int idx)
384      {
385        HashEntry e = new HashEntry(key, value);
386    
387      e.next = buckets[idx];      e.next = buckets[idx];
388      buckets[idx] = e;      buckets[idx] = e;
       
     return null;  
389    }    }
390    
391    /**    /**
392     * removes from the HashMap and returns the value which is mapped by the     * Removes from the HashMap and returns the value which is mapped by the
393     * supplied key; if the key maps to nothing, then the HashMap remains unchanged,     * supplied key. If the key maps to nothing, then the HashMap remains
394     * and <pre>null</pre> is returned     * unchanged, and <pre>null</pre> is returned. NOTE: Since the value
395       * could also be null, you must use containsKey to see if you are
396       * actually removing a mapping.
397     *     *
398     * @param    key     the key used to locate the value to remove from the HashMap     * @param key the key used to locate the value to remove
399       * @return whatever the key mapped to, if present
400     */     */
401    public Object remove(Object key)    public Object remove(Object key)
402    {    {
403      modCount++;      modCount++;
404      int idx = hash(key);      int idx = hash(key);
405      Entry e = buckets[idx];      HashEntry e = buckets[idx];
406      Entry last = null;      HashEntry last = null;
407    
408      while (e != null)      while (e != null)
409        {        {
410          if (key == null ? e.key == null : key.equals(e.key))          if (key == null ? e.key == null : key.equals(e.key))
411            {            {
412              if (last == null)              if (last == null)
413                buckets[idx] = e.next;                buckets[idx] = e.next;
414              else              else
415                last.next = e.next;                last.next = e.next;
416              size--;              size--;
417              return e.value;              // Method call necessary for LinkedHashMap to work correctly.
418            }              return e.cleanup();
419          last = e;            }
420          e = e.next;          last = e;
421            e = e.next;
422        }        }
423      return null;      return null;
424    }    }
425    
426      /**
427       * Copies all elements of the given map into this hashtable.  If this table
428       * already has a mapping for a key, the new mapping replaces the current
429       * one.
430       *
431       * @param m the map to be hashed into this
432       */
433    public void putAll(Map m)    public void putAll(Map m)
434    {    {
     int msize = m.size();  
435      Iterator itr = m.entrySet().iterator();      Iterator itr = m.entrySet().iterator();
436        
437      for (int i=0; i < msize; i++)      for (int msize = m.size(); msize > 0; msize--)
438        {        {
439          Map.Entry e = (Map.Entry) itr.next();          Map.Entry e = (Map.Entry) itr.next();
440          // Optimize in case the Entry is one of our own.          // Optimize in case the Entry is one of our own.
441          if (e instanceof BasicMapEntry)          if (e instanceof BasicMapEntry)
442            {            {
443              BasicMapEntry entry = (BasicMapEntry) e;              BasicMapEntry entry = (BasicMapEntry) e;
444              put(entry.key, entry.value);              put(entry.key, entry.value);
445            }            }
446          else          else
447            {            {
448              put(e.getKey(), e.getValue());              put(e.getKey(), e.getValue());
449            }            }
450        }        }
451    }    }
452      
453      /**
454       * Clears the Map so it has no keys. This is O(1).
455       */
456    public void clear()    public void clear()
457    {    {
458      modCount++;      modCount++;
459      buckets = new Entry[buckets.length];      Arrays.fill(buckets, null);
460      size = 0;      size = 0;
461    }    }
462    
463    /**    /**
464     * returns a shallow clone of this HashMap (i.e. the Map itself is cloned, but     * Returns a shallow clone of this HashMap. The Map itself is cloned,
465     * its contents are not)     * but its contents are not.  This is O(n).
466       *
467       * @return the clone
468     */     */
469    public Object clone()    public Object clone()
470    {    {
# Line 359  public class HashMap extends AbstractMap Line 475  public class HashMap extends AbstractMap
475        }        }
476      catch (CloneNotSupportedException x)      catch (CloneNotSupportedException x)
477        {        {
478            // This is impossible.
479        }        }
480      copy.buckets = new Entry[buckets.length];      copy.buckets = new HashEntry[buckets.length];
481        
482      for (int i=0; i < buckets.length; i++)      for (int i = buckets.length - 1; i >= 0; i--)
483        {        {
484          Entry e = buckets[i];          HashEntry e = buckets[i];
485          Entry last = null;          HashEntry last = null;
486    
487            // Since LinkedHashMap does not override clone, we must clone
488            // the HashEntries to get the one of the correct type.
489          while (e != null)          while (e != null)
490            {            {
491              if (last == null)              if (last == null)
492                {                {
493                  last = new Entry(e.key, e.value);                  last = (HashEntry) e.clone();
494                  copy.buckets[i] = last;                  copy.buckets[i] = last;
495                }                }
496              else              else
497                {                {
498                  last.next = new Entry(e.key, e.value);                  last.next = (HashEntry) e.clone();
499                  last = last.next;                  last = last.next;
500                }                }
501              e = e.next;              e = e.next;
502            }            }
503        }        }
504    
505        // Perform extra bookkeeping required by LinkedHashMap.
506        if (this instanceof LinkedHashMap)
507          ((LinkedHashMap) this).rethread();
508    
509      return copy;      return copy;
510    }    }
511    
512    /** returns a "set view" of this HashMap's keys */    /**
513       * Returns a "set view" of this HashMap's keys. The set is backed by the
514       * HashMap, so changes in one show up in the other.  The set supports
515       * element removal, but not element addition.
516       *
517       * @return a set view of the keys
518       * @see #values()
519       * @see #entrySet()
520       */
521    public Set keySet()    public Set keySet()
522    {    {
523      // Create an AbstractSet with custom implementations of those methods that      // Create an AbstractSet with custom implementations of those methods that
524      // can be overriden easily and efficiently.      // can be overridden easily and efficiently.
525      return new AbstractSet()      return new AbstractSet()
526      {      {
527        public int size()        public int size()
528        {        {
529          return size;          return size;
530        }        }
531          
532        public Iterator iterator()        public Iterator iterator()
533        {        {
534          return new HashIterator(HashIterator.KEYS);          // Cannot create the iterator directly, because of LinkedHashMap.
535            return HashMap.this.iterator(KEYS);
536        }        }
537                
538        public void clear()        public void clear()
539        {        {
540          HashMap.this.clear();          HashMap.this.clear();
# Line 411  public class HashMap extends AbstractMap Line 544  public class HashMap extends AbstractMap
544        {        {
545          return HashMap.this.containsKey(o);          return HashMap.this.containsKey(o);
546        }        }
547          
548        public boolean remove(Object o)        public boolean remove(Object o)
549        {        {
550          // Test against the size of the HashMap to determine if anything          // Test against the size of the HashMap to determine if anything
551          // really got removed. This is neccessary because the return value of          // really got removed. This is neccessary because the return value of
552          // HashMap.remove() is ambiguous in the null case.          // HashMap.remove() is ambiguous in the null case.
553          int oldsize = size;          int oldsize = size;
554          HashMap.this.remove(o);          HashMap.this.remove(o);
555          return (oldsize != size);          return (oldsize != size);
556        }        }
557      };      };
558    }    }
559      
560    /** Returns a "collection view" (or "bag view") of this HashMap's values. */    /**
561       * Returns a "collection view" (or "bag view") of this HashMap's values.
562       * The collection is backed by the HashMap, so changes in one show up
563       * in the other.  The collection supports element removal, but not element
564       * addition.
565       *
566       * @return a bag view of the values
567       * @see #keySet()
568       * @see #entrySet()
569       */
570    public Collection values()    public Collection values()
571    {    {
572      // 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 435  public class HashMap extends AbstractMap Line 577  public class HashMap extends AbstractMap
577        {        {
578          return size;          return size;
579        }        }
580          
581        public Iterator iterator()        public Iterator iterator()
582        {        {
583          return new HashIterator(HashIterator.VALUES);          // Cannot create the iterator directly, because of LinkedHashMap.
584            return HashMap.this.iterator(VALUES);
585        }        }
586          
587        public void clear()        public void clear()
588        {        {
589          HashMap.this.clear();          HashMap.this.clear();
# Line 448  public class HashMap extends AbstractMap Line 591  public class HashMap extends AbstractMap
591      };      };
592    }    }
593    
594    /** Returns a "set view" of this HashMap's entries. */    /**
595       * Returns a "set view" of this HashMap's entries. The set is backed by
596       * the HashMap, so changes in one show up in the other.  The set supports
597       * element removal, but not element addition.
598       * <p>
599       *
600       * Note that the iterators for all three views, from keySet(), entrySet(),
601       * and values(), traverse the HashMap in the same sequence.
602       *
603       * @return a set view of the entries
604       * @see #keySet()
605       * @see #values()
606       * @see Map.Entry
607       */
608    public Set entrySet()    public Set entrySet()
609    {    {
610      // Create an AbstractSet with custom implementations of those methods that      // Create an AbstractSet with custom implementations of those methods that
611      // can be overriden easily and efficiently.      // can be overridden easily and efficiently.
612      return new AbstractSet()      return new AbstractSet()
613      {      {
614        public int size()        public int size()
615        {        {
616          return size;          return size;
617        }        }
618          
619        public Iterator iterator()        public Iterator iterator()
620        {        {
621          return new HashIterator(HashIterator.ENTRIES);          // Cannot create the iterator directly, because of LinkedHashMap.
622            return HashMap.this.iterator(ENTRIES);
623        }        }
624                
625        public void clear()        public void clear()
626        {        {
627          HashMap.this.clear();          HashMap.this.clear();
# Line 472  public class HashMap extends AbstractMap Line 629  public class HashMap extends AbstractMap
629    
630        public boolean contains(Object o)        public boolean contains(Object o)
631        {        {
632          if (!(o instanceof Map.Entry))          return getEntry(o) != null;
           return false;  
         Map.Entry me = (Map.Entry) o;  
         Entry e = getEntry(me);  
         return (e != null);  
633        }        }
634          
635        public boolean remove(Object o)        public boolean remove(Object o)
636        {        {
637          if (!(o instanceof Map.Entry))          HashEntry e = getEntry(o);
638            return false;          if (e != null)
639          Map.Entry me = (Map.Entry) o;            {
640          Entry e = getEntry(me);              HashMap.this.remove(e.key);
641          if (e != null)              return true;
642            {            }
643              HashMap.this.remove(e.key);          return false;
             return true;  
           }  
         return false;  
644        }        }
645      };      };
646    }    }
     
   /** Return an index in the buckets array for `key' based on its hashCode() */  
   private int hash(Object key)  
   {  
     if (key == null)  
       return 0;  
     else  
       return Math.abs(key.hashCode() % buckets.length);  
   }  
647    
648    /** Return an Entry who's key and value equal the supplied Map.Entry.    /** Helper method that returns an index in the buckets array for `key;
649      * This is used by entrySet's contains() and remove() methods. They can't     * based on its hashCode().
650      * use contains(key) and remove(key) directly because that would result     *
651      * in entries with the same key but a different value being matched. */     * @param key the key
652    private Entry getEntry(Map.Entry me)     * @return the bucket number
653       */
654      int hash(Object key)
655    {    {
656        return (key == null) ? 0 : Math.abs(key.hashCode() % buckets.length);
657      }
658    
659      /**
660       * Helper method for entrySet(), which matches both key and value
661       * simultaneously.
662       *
663       * @param o the entry to match
664       * @return the matching entry, if found, or null
665       * @see #entrySet()
666       */
667      private HashEntry getEntry(Object o)
668      {
669        if (!(o instanceof Map.Entry))
670          return null;
671        Map.Entry me = (Map.Entry) o;
672      int idx = hash(me.getKey());      int idx = hash(me.getKey());
673      Entry e = buckets[idx];      HashEntry e = buckets[idx];
674      while (e != null)      while (e != null)
675        {        {
676          if (e.equals(me))          if (e.equals(me))
677            return e;            return e;
678          e = e.next;          e = e.next;
679        }        }
680      return null;      return null;
681    }    }
682      
683    /**    /**
684     * increases the size of the HashMap and rehashes all keys to new array     * Increases the size of the HashMap and rehashes all keys to new array
685     * 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
686     * size() > threshold. Note that the existing Entry objects are reused in     * size() > threshold. Note that the existing Entry objects are reused in
687     * the new hash table.     * the new hash table.
688       * <p>
689       *
690       * This is not specified, but the new size is twice the current size plus
691       * one; this number is not always prime, unfortunately.
692     */     */
693    private void rehash()    private void rehash()
694    {    {
695      Entry[] oldBuckets = buckets;      HashEntry[] oldBuckets = buckets;
696        
697      int newcapacity = (buckets.length * 2) + 1;      int newcapacity = (buckets.length * 2) + 1;
698      threshold = (int) (newcapacity * loadFactor);      threshold = (int) (newcapacity * loadFactor);
699      buckets = new Entry[newcapacity];      buckets = new HashEntry[newcapacity];
700        
701      for (int i = 0; i < oldBuckets.length; i++)      for (int i = oldBuckets.length - 1; i >= 0; i--)
702        {        {
703          Entry e = oldBuckets[i];          HashEntry e = oldBuckets[i];
704          while (e != null)          while (e != null)
705            {            {
706              int idx = hash(e.key);              int idx = hash(e.key);
707              Entry dest = buckets[idx];              HashEntry dest = buckets[idx];
708    
709              if (dest != null)              if (dest != null)
710                {                {
711                  while (dest.next != null)                  while (dest.next != null)
712                    dest = dest.next;                    dest = dest.next;
713                  dest.next = e;                  dest.next = e;
714                }                }
715              else              else
716                {                {
717                  buckets[idx] = e;                  buckets[idx] = e;
718                }                }
719    
720              Entry next = e.next;              HashEntry next = e.next;
721              e.next = null;              e.next = null;
722              e = next;              e = next;
723            }            }
724        }        }
725    }    }
726    
727    /**    /**
728       * Generates a parameterized iterator.  Must be overrideable, since
729       * LinkedHashMap iterates in a different order.
730       * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
731       * @return the appropriate iterator
732       */
733      Iterator iterator(int type)
734      {
735        return new HashIterator(type);
736      }
737    
738      /**
739     * Serializes this object to the given stream.     * Serializes this object to the given stream.
740     * @serialdata the <i>capacity</i>(int) that is the length of the     *
741     * bucket array, the <i>size</i>(int) of the hash map are emitted     * @param s the stream to write to
742     * first.  They are followed by size entries, each consisting of     * @throws IOException if the underlying stream fails
743     * a key (Object) and a value (Object).     * @serialData the <i>capacity</i>(int) that is the length of the
744       *             bucket array, the <i>size</i>(int) of the hash map
745       *             are emitted first.  They are followed by size entries,
746       *             each consisting of a key (Object) and a value (Object).
747     */     */
748    private void writeObject(ObjectOutputStream s) throws IOException    private void writeObject(ObjectOutputStream s) throws IOException
749    {    {
750      // the threshold and loadFactor fields      // Write the threshold and loadFactor fields.
751      s.defaultWriteObject();      s.defaultWriteObject();
752    
753      s.writeInt(buckets.length);      s.writeInt(buckets.length);
754      s.writeInt(size);      s.writeInt(size);
755      Iterator it = entrySet().iterator();      // Avoid creating a wasted Set by creating the iterator directly.
756        Iterator it = iterator(ENTRIES);
757      while (it.hasNext())      while (it.hasNext())
758        {        {
759          Map.Entry entry = (Map.Entry) it.next();          HashEntry entry = (HashEntry) it.next();
760          s.writeObject(entry.getKey());          s.writeObject(entry.key);
761          s.writeObject(entry.getValue());          s.writeObject(entry.value);
762        }        }
763    }    }
764    
765    /**    /**
766     * Deserializes this object from the given stream.     * Deserializes this object from the given stream.
767     * @serialdata the <i>capacity</i>(int) that is the length of the     *
768     * bucket array, the <i>size</i>(int) of the hash map are emitted     * @param s the stream to read from
769     * first.  They are followed by size entries, each consisting of     * @throws ClassNotFoundException if the underlying stream fails
770     * a key (Object) and a value (Object).     * @throws IOException if the underlying stream fails
771       * @serialData the <i>capacity</i>(int) that is the length of the
772       *             bucket array, the <i>size</i>(int) of the hash map
773       *             are emitted first.  They are followed by size entries,
774       *             each consisting of a key (Object) and a value (Object).
775     */     */
776    private void readObject(ObjectInputStream s)    private void readObject(ObjectInputStream s)
777      throws IOException, ClassNotFoundException      throws IOException, ClassNotFoundException
778    {    {
779      // the threshold and loadFactor fields      // Read the threshold and loadFactor fields.
780      s.defaultReadObject();      s.defaultReadObject();
781    
782      int capacity = s.readInt();      // Read and use capacity.
783        buckets = new HashEntry[s.readInt()];
784      int len = s.readInt();      int len = s.readInt();
785      size = 0;      // Already happens automatically.
786      modCount = 0;      // size = 0;
787      buckets = new Entry[capacity];      // modCount = 0;
788    
789      for (int i = 0; i < len; i++)      // Read and use key/value pairs.
790        {      for ( ; len > 0; len--)
791          Object key = s.readObject();        put(s.readObject(), s.readObject());
         Object value = s.readObject();  
         put(key, value);  
       }  
792    }    }
793    
794    /**    /**
# Line 616  public class HashMap extends AbstractMap Line 796  public class HashMap extends AbstractMap
796     * This implementation is parameterized to give a sequential view of     * This implementation is parameterized to give a sequential view of
797     * keys, values, or entries.     * keys, values, or entries.
798     *     *
799     * @author       Jon Zeppieri     * @author Jon Zeppieri
800     */     */
801    class HashIterator implements Iterator    class HashIterator implements Iterator
802    {    {
803      static final int KEYS = 0,      /**
804                       VALUES = 1,       * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
805                       ENTRIES = 2;       * or {@link #ENTRIES}.
806                             */
807      // the type of this Iterator: KEYS, VALUES, or ENTRIES.      final int type;
808      int type;      /**
809      // the number of modifications to the backing Hashtable that we know about.       * The number of modifications to the backing HashMap that we know about.
810      int knownMod;       */
811      // The total number of elements returned by next(). Used to determine if      int knownMod = modCount;
812      // there are more elements remaining.      /** The number of elements remaining to be returned by next(). */
813      int count;      int count = size;
814      // Current index in the physical hash table.      /** Current index in the physical hash table. */
815      int idx;      int idx = buckets.length;
816      // The last Entry returned by a next() call.      /** The last Entry returned by a next() call. */
817      Entry last;      HashEntry last;
818      // The next entry that should be returned by next(). It is set to something      /**
819      // 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
820      // entries. It is null if next() needs to find a new bucket.       * if we're iterating through a bucket that contains multiple linked
821      Entry next;       * entries. It is null if next() needs to find a new bucket.
822         */
823        HashEntry next;
824    
825      /* construct a new HashtableIterator with the supplied type:      /**
826         KEYS, VALUES, or ENTRIES */       * Construct a new HashIterator with the supplied type.
827         * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
828         */
829      HashIterator(int type)      HashIterator(int type)
830      {      {
831        this.type = type;        this.type = type;
       knownMod = HashMap.this.modCount;  
       count = 0;  
       idx = buckets.length;  
832      }      }
833    
834      /** returns true if the Iterator has more elements */      /**
835         * Returns true if the Iterator has more elements.
836         * @return true if there are more elements
837         * @throws ConcurrentModificationException if the HashMap was modified
838         */
839      public boolean hasNext()      public boolean hasNext()
840      {      {
841        if (knownMod != HashMap.this.modCount)        if (knownMod != modCount)
842          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
843        return count < size;        return count > 0;
844      }      }
845    
846      /** returns the next element in the Iterator's sequential view */      /**
847         * Returns the next element in the Iterator's sequential view.
848         * @return the next element
849         * @throws ConcurrentModificationException if the HashMap was modified
850         * @throws NoSuchElementException if there is none
851         */
852      public Object next()      public Object next()
853      {      {
854        if (knownMod != HashMap.this.modCount)        if (knownMod != modCount)
855          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
856        if (count == size)        if (count == 0)
857          throw new NoSuchElementException();          throw new NoSuchElementException();
858        count++;        count--;
859        Entry e = null;        HashEntry e = next;
       if (next != null)  
         e = next;  
860    
861        while (e == null)        while (e == null)
862          {          e = buckets[--idx];
           e = buckets[--idx];  
         }  
863    
864        next = e.next;        next = e.next;
865        last = e;        last = e;
# Line 684  public class HashMap extends AbstractMap Line 870  public class HashMap extends AbstractMap
870        return e;        return e;
871      }      }
872    
873      /**      /**
874       * removes from the backing HashMap the last element which was fetched with the       * Removes from the backing HashMap the last element which was fetched
875       * <pre>next()</pre> method       * with the <pre>next()</pre> method.
876         * @throws ConcurrentModificationException if the HashMap was modified
877         * @throws IllegalStateException if called when there is no last element
878       */       */
879      public void remove()      public void remove()
880      {      {
881        if (knownMod != HashMap.this.modCount)        if (knownMod != modCount)
882          throw new ConcurrentModificationException();          throw new ConcurrentModificationException();
883        if (last == null)        if (last == null)
884          {          throw new IllegalStateException();
885            throw new IllegalStateException();  
886          }        HashMap.this.remove(last.key);
887        else        knownMod++;
888          {        last = null;
           HashMap.this.remove(last.key);  
           knownMod++;  
           count--;  
           last = null;  
         }  
889      }      }
890    }    }
891  }  }

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

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