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

Diff of /classpath/java/util/LinkedHashMap.java

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

revision 1.5 by brawer, Tue Apr 30 14:05:06 2002 UTC revision 1.6 by ericb, Wed Nov 6 14:03:43 2002 UTC
# Line 1  Line 1 
1  /* LinkedHashMap.java -- a class providing hashtable data structure,  /* LinkedHashMap.java -- a class providing hashtable data structure,
2     mapping Object --> Object, with linked list traversal     mapping Object --> Object, with linked list traversal
3     Copyright (C) 2001 Free Software Foundation, Inc.     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
4    
5  This file is part of GNU Classpath.  This file is part of GNU Classpath.
6    
# Line 50  package java.util; Line 50  package java.util;
50   * can cause primary clustering) and rehashing (which does not fit very   * can cause primary clustering) and rehashing (which does not fit very
51   * well with Java's method of precomputing hash codes) are avoided.  In   * well with Java's method of precomputing hash codes) are avoided.  In
52   * addition, this maintains a doubly-linked list which tracks either   * addition, this maintains a doubly-linked list which tracks either
53   * insertion or access order.  Note that the insertion order is not   * insertion or access order.
54   * modified if a <code>put</code> simply reinserts a key in the map.   * <p>
55     *
56     * In insertion order, calling <code>put</code> adds the key to the end of
57     * traversal, unless the key was already in the map; changing traversal order
58     * requires removing and reinserting a key.  On the other hand, in access
59     * order, all calls to <code>put</code> and <code>get</code> cause the
60     * accessed key to move to the end of the traversal list.  Note that any
61     * accesses to the map's contents via its collection views and iterators do
62     * not affect the map's traversal order, since the collection views do not
63     * call <code>put</code> or <code>get</code>.
64   * <p>   * <p>
65   *   *
66   * One of the nice features of tracking insertion order is that you can   * One of the nice features of tracking insertion order is that you can
# Line 61  package java.util; Line 70  package java.util;
70   * <p>   * <p>
71   *   *
72   * When using this {@link #LinkedHashMap(int, float, boolean) constructor},   * When using this {@link #LinkedHashMap(int, float, boolean) constructor},
73   * you build an access-order mapping.  This can be used to implement LRU   * you can build an access-order mapping.  This can be used to implement LRU
74   * caches, for example.  In this case, every invocation of <code>put</code>,   * caches, for example.  By overriding {@link #removeEldestEntry(Map.Entry)},
75   * <code>putAll</code>, or <code>get</code> moves the accessed entry to   * you can also control the removal of the oldest entry, and thereby do
76   * the end of the iteration list.  By overriding   * things like keep the map at a fixed size.
  * {@link #removeEldestEntry(Map.Entry)}, you can also control the  
  * removal of the oldest entry, and thereby do things like keep the map  
  * at a fixed size.  
77   * <p>   * <p>
78   *   *
79   * Under ideal circumstances (no collisions), LinkedHashMap offers O(1)   * Under ideal circumstances (no collisions), LinkedHashMap offers O(1)
80   * performance on most operations (<code>containsValue()</code> is,   * performance on most operations (<code>containsValue()</code> is,
81   * 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
82   * hash code -- very unlikely), most operations are O(n).   * hash code -- very unlikely), most operations are O(n).  Traversal is
83     * faster than in HashMap (proportional to the map size, and not the space
84     * allocated for the map), but other operations may be slower because of the
85     * overhead of the maintaining the traversal order list.
86   * <p>   * <p>
87   *   *
88   * LinkedHashMap accepts the null key and null values.  It is not   * LinkedHashMap accepts the null key and null values.  It is not
# Line 105  public class LinkedHashMap extends HashM Line 114  public class LinkedHashMap extends HashM
114    private static final long serialVersionUID = 3801124242820219131L;    private static final long serialVersionUID = 3801124242820219131L;
115    
116    /**    /**
117     * The first Entry to iterate over.     * The oldest Entry to begin iteration at.
    */  
   transient LinkedHashEntry head;  
   
   /**  
    * The last Entry to iterate over.  
118     */     */
119    transient LinkedHashEntry tail;    transient LinkedHashEntry root;
120    
121    /**    /**
122     * The iteration order of this linked hash map: <code>true</code> for     * The iteration order of this linked hash map: <code>true</code> for
123     * access-order, <code>false</code> for insertion-order.     * access-order, <code>false</code> for insertion-order.
124     * @serial     *
125       * @serial true for access order traversal
126     */     */
127    final boolean accessOrder;    final boolean accessOrder;
128    
# Line 127  public class LinkedHashMap extends HashM Line 132  public class LinkedHashMap extends HashM
132     */     */
133    class LinkedHashEntry extends HashEntry    class LinkedHashEntry extends HashEntry
134    {    {
135      /** The predecessor in the iteration list, null if this is the eldest. */      /**
136         * The predecessor in the iteration list. If this entry is the root
137         * (eldest), pred points to the newest entry.
138         */
139      LinkedHashEntry pred;      LinkedHashEntry pred;
140    
141      /** The successor in the iteration list, null if this is the newest. */      /** The successor in the iteration list, null if this is the newest. */
142      LinkedHashEntry succ;      LinkedHashEntry succ;
143    
144      /**      /**
145       * Simple constructor.       * Simple constructor.
146         *
147       * @param key the key       * @param key the key
148       * @param value the value       * @param value the value
149       */       */
150      LinkedHashEntry(Object key, Object value)      LinkedHashEntry(Object key, Object value)
151      {      {
152        super(key, value);        super(key, value);
153        if (head == null)        if (root == null)
154          head = this;          {
155        pred = tail;            root = this;
156        tail = this;            pred = this;
157        if (pred != null)          }
158          pred.succ = this;        else
159            {
160              pred = root.pred;
161              pred.succ = this;
162              root.pred = this;
163            }
164      }      }
165    
166      /**      /**
167       * Sets the value of this entry, and shuffles it to the end of       * Called when this entry is accessed via put or get. This version does
168       * the list if this is in access-order.       * the necessary bookkeeping to keep the doubly-linked list in order,
169       * @param value the new value       * after moving this element to the newest position in access order.
      * @return the prior value  
170       */       */
171      public Object setValue(Object value)      void access()
172      {      {
173        if (accessOrder && succ != null)        if (accessOrder && succ != null)
174          {          {
175            succ.pred = pred;            modCount++;
176            if (pred == null)            if (this == root)
177              head = succ;              {
178                  root = succ;
179                  pred.succ = this;
180                  succ = null;
181                }
182            else            else
183              pred.succ = succ;              {
184            succ = null;                pred.succ = succ;
185            pred = tail;                succ.pred = pred;
186            pred.succ = this;                succ = null;
187            tail = this;                pred = root.pred;
188                  pred.succ = this;
189                }
190          }          }
       return super.setValue(value);  
191      }      }
192    
193      /**      /**
194       * Called when this entry is removed from the map. This version does       * Called when this entry is removed from the map. This version does
195       * the necessary bookkeeping to keep the doubly-linked list in order.       * the necessary bookkeeping to keep the doubly-linked list in order.
196         *
197       * @return the value of this key as it is removed       * @return the value of this key as it is removed
198       */       */
199      Object cleanup()      Object cleanup()
200      {      {
201        if (pred == null)        if (this == root)
202          head = succ;          {
203        else            root = succ;
204          pred.succ = succ;            if (succ != null)
205        if (succ == null)              succ.pred = pred;
206          tail = pred;          }
207          else if (succ == null)
208            {
209              pred.succ = null;
210              root.pred = pred;
211            }
212        else        else
213          succ.pred = pred;          {
214                    pred.succ = succ;
215              succ.pred = pred;
216            }
217        return value;        return value;
218      }      }
219    }    } // class LinkedHashEntry
220    
221    /**    /**
222     * Construct a new insertion-ordered LinkedHashMap with the default     * Construct a new insertion-ordered LinkedHashMap with the default
# Line 253  public class LinkedHashMap extends HashM Line 280  public class LinkedHashMap extends HashM
280     * Construct a new LinkedHashMap with a specific inital capacity, load     * Construct a new LinkedHashMap with a specific inital capacity, load
281     * factor, and ordering mode.     * factor, and ordering mode.
282     *     *
283     * @param initialCapacity the initial capacity (>=0)     * @param initialCapacity the initial capacity (&gt;=0)
284     * @param loadFactor the load factor (>0, not NaN)     * @param loadFactor the load factor (&gt;0, not NaN)
285     * @param accessOrder true for access-order, false for insertion-order     * @param accessOrder true for access-order, false for insertion-order
    *  
286     * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||     * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
287     *                                     ! (loadFactor &gt; 0.0)     *                                     ! (loadFactor &gt; 0.0)
288     */     */
# Line 273  public class LinkedHashMap extends HashM Line 299  public class LinkedHashMap extends HashM
299    public void clear()    public void clear()
300    {    {
301      super.clear();      super.clear();
302      head = null;      root = null;
     tail = null;  
303    }    }
304    
305    /**    /**
# Line 282  public class LinkedHashMap extends HashM Line 307  public class LinkedHashMap extends HashM
307     * <code>o</code>, such that <code>o.equals(value)</code>.     * <code>o</code>, such that <code>o.equals(value)</code>.
308     *     *
309     * @param value the value to search for in this HashMap     * @param value the value to search for in this HashMap
    *  
310     * @return <code>true</code> if at least one key maps to the value     * @return <code>true</code> if at least one key maps to the value
311     */     */
312    public boolean containsValue(Object value)    public boolean containsValue(Object value)
313    {    {
314      LinkedHashEntry e = head;      LinkedHashEntry e = root;
315      while (e != null)      while (e != null)
316        {        {
317          if (equals(value, e.value))          if (equals(value, e.value))
# Line 318  public class LinkedHashMap extends HashM Line 342  public class LinkedHashMap extends HashM
342        {        {
343          if (equals(key, e.key))          if (equals(key, e.key))
344            {            {
345              if (accessOrder)              e.access();
               {  
                 modCount++;  
                 LinkedHashEntry l = (LinkedHashEntry) e;  
                 if (l.succ != null)  
                   {  
                     l.succ.pred = l.pred;  
                     if (l.pred == null)  
                       head = l.succ;  
                     else  
                       l.pred.succ = l.succ;  
                     l.succ = null;  
                     l.pred = tail;  
                     tail.succ = l;  
                     tail = l;  
                   }  
               }  
346              return e.value;              return e.value;
347            }            }
348          e = e.next;          e = e.next;
# Line 352  public class LinkedHashMap extends HashM Line 360  public class LinkedHashMap extends HashM
360     * <p>     * <p>
361     *     *
362     * For example, to keep the Map limited to 100 entries, override as follows:     * For example, to keep the Map limited to 100 entries, override as follows:
363     *     * <pre>
364  <pre>private static final int MAX_ENTRIES = 100;     * private static final int MAX_ENTRIES = 100;
365       * protected boolean removeEldestEntry(Map.Entry eldest)
366  protected boolean removeEldestEntry(Map.Entry eldest)     * {
367  {     *   return size() &gt; MAX_ENTRIES;
368    return size() &gt; MAX_ENTRIES;     * }
369  }     * </pre><p>
 </pre><p>  
370     *     *
371     * Typically, this method does not modify the map, but just uses the     * Typically, this method does not modify the map, but just uses the
372     * return value as an indication to <code>put</code> whether to proceed.     * return value as an indication to <code>put</code> whether to proceed.
373     * However, if you override it to modify the map, you must return false     * However, if you override it to modify the map, you must return false
374     * (indicating that <code>put</code> should do nothing), or face     * (indicating that <code>put</code> should leave the modified map alone),
375     * unspecified behavior.     * or you face unspecified behavior.  Remember that in access-order mode,
376       * even calling <code>get</code> is a structural modification, but using
377       * the collections views (such as <code>keySet</code>) is not.
378     * <p>     * <p>
379     *     *
380     * This method is called after the eldest entry has been inserted, so     * This method is called after the eldest entry has been inserted, so
# Line 378  protected boolean removeEldestEntry(Map. Line 387  protected boolean removeEldestEntry(Map.
387     *        returns true. For an access-order map, this is the least     *        returns true. For an access-order map, this is the least
388     *        recently accessed; for an insertion-order map, this is the     *        recently accessed; for an insertion-order map, this is the
389     *        earliest element inserted.     *        earliest element inserted.
    *  
390     * @return true if <code>eldest</code> should be removed     * @return true if <code>eldest</code> should be removed
391     */     */
392    protected boolean removeEldestEntry(Map.Entry eldest)    protected boolean removeEldestEntry(Map.Entry eldest)
# Line 396  protected boolean removeEldestEntry(Map. Line 404  protected boolean removeEldestEntry(Map.
404     * @param callRemove whether to call the removeEldestEntry method     * @param callRemove whether to call the removeEldestEntry method
405     * @see #put(Object, Object)     * @see #put(Object, Object)
406     * @see #removeEldestEntry(Map.Entry)     * @see #removeEldestEntry(Map.Entry)
407       * @see LinkedHashEntry#LinkedHashEntry(Object, Object)
408     */     */
409    void addEntry(Object key, Object value, int idx, boolean callRemove)    void addEntry(Object key, Object value, int idx, boolean callRemove)
410    {    {
411      LinkedHashEntry e = new LinkedHashEntry(key, value);      LinkedHashEntry e = new LinkedHashEntry(key, value);
   
412      e.next = buckets[idx];      e.next = buckets[idx];
413      buckets[idx] = e;      buckets[idx] = e;
414        if (callRemove && removeEldestEntry(root))
415      if (callRemove && removeEldestEntry(head))        remove(root);
       remove(head);  
416    }    }
417    
418    /**    /**
419     * Helper method, called by clone() to reset the doubly-linked list.     * Helper method, called by clone() to reset the doubly-linked list.
420       *
421     * @param m the map to add entries from     * @param m the map to add entries from
422     * @see #clone()     * @see #clone()
423     */     */
424    void putAllInternal(Map m)    void putAllInternal(Map m)
425    {    {
426      head = null;      root = null;
     tail = null;  
427      super.putAllInternal(m);      super.putAllInternal(m);
428    }    }
429    
430    /**    /**
431     * Generates a parameterized iterator. This allows traversal to follow     * Generates a parameterized iterator. This allows traversal to follow
432     * the doubly-linked list instead of the random bin order of HashMap.     * the doubly-linked list instead of the random bin order of HashMap.
433       *
434     * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}     * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
435     * @return the appropriate iterator     * @return the appropriate iterator
436     */     */
# Line 430  protected boolean removeEldestEntry(Map. Line 438  protected boolean removeEldestEntry(Map.
438    {    {
439      return new Iterator()      return new Iterator()
440      {      {
441        /** The current Entry */        /** The current Entry. */
442        LinkedHashEntry current = head;        LinkedHashEntry current = root;
443    
444        /** The previous Entry returned by next() */        /** The previous Entry returned by next(). */
445        LinkedHashEntry last;        LinkedHashEntry last;
446    
447        /** The number of known modifications to the backing HashMap */        /** The number of known modifications to the backing Map. */
448        int knownMod = modCount;        int knownMod = modCount;
449    
450        /**        /**
451         * Returns true if the Iterator has more elements.         * Returns true if the Iterator has more elements.
452           *
453         * @return true if there are more elements         * @return true if there are more elements
454         * @throws ConcurrentModificationException if the HashMap was modified         * @throws ConcurrentModificationException if the HashMap was modified
455         */         */
# Line 453  protected boolean removeEldestEntry(Map. Line 462  protected boolean removeEldestEntry(Map.
462    
463        /**        /**
464         * Returns the next element in the Iterator's sequential view.         * Returns the next element in the Iterator's sequential view.
465           *
466         * @return the next element         * @return the next element
467         * @throws ConcurrentModificationException if the HashMap was modified         * @throws ConcurrentModificationException if the HashMap was modified
468         * @throws NoSuchElementException if there is none         * @throws NoSuchElementException if there is none
# Line 473  protected boolean removeEldestEntry(Map. Line 483  protected boolean removeEldestEntry(Map.
483         * with the <code>next()</code> method.         * with the <code>next()</code> method.
484         *         *
485         * @throws ConcurrentModificationException if the HashMap was modified         * @throws ConcurrentModificationException if the HashMap was modified
        *  
486         * @throws IllegalStateException if called when there is no last element         * @throws IllegalStateException if called when there is no last element
487         */         */
488        public void remove()        public void remove()
# Line 482  protected boolean removeEldestEntry(Map. Line 491  protected boolean removeEldestEntry(Map.
491            throw new ConcurrentModificationException();            throw new ConcurrentModificationException();
492          if (last == null)          if (last == null)
493            throw new IllegalStateException();            throw new IllegalStateException();
   
494          LinkedHashMap.this.remove(last.key);          LinkedHashMap.this.remove(last.key);
495          last = null;          last = null;
496          knownMod++;          knownMod++;
497        }        }
498      };      };
499    }    }
500  }  } // class LinkedHashMap

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.6

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