jdk/src/share/classes/com/sun/tools/jdi/LinkedHashMap.java
changeset 11382 9bd385be6f03
parent 11381 890ea587d133
parent 11325 0ff7113a0882
child 11383 ae090403accf
equal deleted inserted replaced
11381:890ea587d133 11382:9bd385be6f03
     1 /*
       
     2  * Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package com.sun.tools.jdi;
       
    26 
       
    27 import java.io.*;
       
    28 import java.util.*;
       
    29 
       
    30 /**
       
    31  * Hash table based implementation of the Map interface.  This implementation
       
    32  * provides all of the optional Map operations, and permits null values and
       
    33  * the null key.  (HashMap is roughly equivalent to Hashtable, except that it
       
    34  * is unsynchronized and permits nulls.) In addition, elements in the map are
       
    35  * ordered and doubly linked together.
       
    36  * <p>
       
    37  * This implementation provides constant-time performance for the basic
       
    38  * operations (get and put), assuming the the hash function disperses the
       
    39  * elements properly among the buckets.  Iteration over Collection views
       
    40  * requires time proportional to its size (the number of key-value mappings)
       
    41  * and returns elements in the order they are linked. In a HashMap the
       
    42  * iteration would require time  proportional to the capacity of the map
       
    43  * plus the map size.
       
    44  * <p>
       
    45  * An instance of LinkedHashMap has two parameters that affect its efficiency:
       
    46  * its <i>capacity</i> and its <i>load factor</i>. The load factor should be
       
    47  * between 0.0 and 1.0. When the number of mappings in the LinkedHashMap exceeds
       
    48  * the product of the load factor and the current capacity, the capacity is
       
    49  * increased by calling the rehash method which requires time proportional
       
    50  * to the number of key-value mappings in the map. Larger load factors
       
    51  * use memory more efficiently, at the expense of larger expected time per
       
    52  * lookup.
       
    53  * <p>
       
    54  * If many mappings are to be stored in a LinkedHashMap, creating it with a
       
    55  * sufficiently large capacity will allow the mappings to be stored more
       
    56  * efficiently than letting it perform automatic rehashing as needed to grow
       
    57  * the table.
       
    58  * <p>
       
    59  * <strong>Note that this implementation is not synchronized.</strong> If
       
    60  * multiple threads access a LinkedHashMap concurrently, and at least one of the
       
    61  * threads modifies the LinkedHashMap structurally, it <em>must</em> be
       
    62  * synchronized externally.  (A structural modification is any operation that
       
    63  * adds or deletes one or more mappings; merely changing the value associated
       
    64  * with a key that is already contained in the Table is not a structural
       
    65  * modification.)  This is typically accomplished by synchronizing on some
       
    66  * object that naturally encapsulates the LinkedHashMap.  If no such object
       
    67  * exists, the LinkedHashMap should be "wrapped" using the
       
    68  * Collections.synchronizedSet method.  This is best done at creation time, to
       
    69  * prevent accidental unsynchronized access to the LinkedHashMap:
       
    70  * <pre>
       
    71  *      Map m = Collections.synchronizedMap(new LinkedHashMap(...));
       
    72  * </pre>
       
    73  * <p>
       
    74  * The Iterators returned by the iterator methods of the Collections returned
       
    75  * by all of LinkedHashMap's "collection view methods" are <em>fail-fast</em>:
       
    76  * if the LinkedHashMap is structurally modified at any time after the Iterator
       
    77  * is created, in any way except through the Iterator's own remove or add
       
    78  * methods, the Iterator will throw a ConcurrentModificationException.  Thus,
       
    79  * in the face of concurrent modification, the Iterator fails quickly and
       
    80  * cleanly, rather than risking arbitrary, non-deterministic behavior at an
       
    81  * undetermined time in the future.
       
    82  *
       
    83  * @author  Josh Bloch
       
    84  * @author  Arthur van Hoff
       
    85  * @author  Zhenghua Li
       
    86  * @see     Object#hashCode()
       
    87  * @see     java.util.Collection
       
    88  * @see     java.util.Map
       
    89  * @see     java.util.TreeMap
       
    90  * @see     java.util.Hashtable
       
    91  * @see     java.util.HashMap
       
    92  */
       
    93 
       
    94 import java.io.Serializable;
       
    95 
       
    96 public class LinkedHashMap extends AbstractMap implements Map, Serializable {
       
    97     /**
       
    98      * The hash table data.
       
    99      */
       
   100     private transient Entry table[];
       
   101 
       
   102     /**
       
   103      * The head of the double linked list.
       
   104      */
       
   105     private transient Entry header;
       
   106 
       
   107     /**
       
   108      * The total number of mappings in the hash table.
       
   109      */
       
   110     private transient int count;
       
   111 
       
   112     /**
       
   113      * Rehashes the table when count exceeds this threshold.
       
   114      */
       
   115     private int threshold;
       
   116 
       
   117     /**
       
   118      * The load factor for the LinkedHashMap.
       
   119      */
       
   120     private float loadFactor;
       
   121 
       
   122     /**
       
   123      * The number of times this LinkedHashMap has been structurally modified
       
   124      * Structural modifications are those that change the number of mappings in
       
   125      * the LinkedHashMap or otherwise modify its internal structure (e.g.,
       
   126      * rehash).  This field is used to make iterators on Collection-views of
       
   127      * the LinkedHashMap fail-fast.  (See ConcurrentModificationException).
       
   128      */
       
   129     private transient int modCount = 0;
       
   130 
       
   131     /**
       
   132      * Constructs a new, empty LinkedHashMap with the specified initial
       
   133      * capacity and the specified load factor.
       
   134      *
       
   135      * @param      initialCapacity   the initial capacity of the LinkedHashMap.
       
   136      * @param      loadFactor        a number between 0.0 and 1.0.
       
   137      * @exception  IllegalArgumentException  if the initial capacity is less
       
   138      *               than or equal to zero, or if the load factor is less than
       
   139      *               or equal to zero.
       
   140      */
       
   141     public LinkedHashMap(int initialCapacity, float loadFactor) {
       
   142         if (initialCapacity < 0)
       
   143             throw new IllegalArgumentException("Illegal Initial Capacity: "+
       
   144                                                initialCapacity);
       
   145         if ((loadFactor > 1) || (loadFactor <= 0))
       
   146             throw new IllegalArgumentException("Illegal Load factor: "+
       
   147                                                loadFactor);
       
   148         if (initialCapacity==0)
       
   149             initialCapacity = 1;
       
   150         this.loadFactor = loadFactor;
       
   151         table = new Entry[initialCapacity];
       
   152         threshold = (int)(initialCapacity * loadFactor);
       
   153         header = new Entry(-1, null, null, null);
       
   154         header.before = header.after = header;
       
   155     }
       
   156 
       
   157     /**
       
   158      * Constructs a new, empty LinkedHashMap with the specified initial capacity
       
   159      * and default load factor.
       
   160      *
       
   161      * @param   initialCapacity   the initial capacity of the LinkedHashMap.
       
   162      */
       
   163     public LinkedHashMap(int initialCapacity) {
       
   164         this(initialCapacity, 0.75f);
       
   165     }
       
   166 
       
   167     /**
       
   168      * Constructs a new, empty LinkedHashMap with a default capacity and load
       
   169      * factor.
       
   170      */
       
   171     public LinkedHashMap() {
       
   172         this(101, 0.75f);
       
   173     }
       
   174 
       
   175     /**
       
   176      * Constructs a new LinkedHashMap with the same mappings as the given
       
   177      * Map.  The LinkedHashMap is created with a capacity of thrice the number
       
   178      * of mappings in the given Map or 11 (whichever is greater), and a
       
   179      * default load factor.
       
   180      */
       
   181     public LinkedHashMap(Map t) {
       
   182         this(Math.max(3*t.size(), 11), 0.75f);
       
   183         putAll(t);
       
   184     }
       
   185 
       
   186     /**
       
   187      * Returns the number of key-value mappings in this Map.
       
   188      */
       
   189     public int size() {
       
   190         return count;
       
   191     }
       
   192 
       
   193     /**
       
   194      * Returns true if this Map contains no key-value mappings.
       
   195      */
       
   196     public boolean isEmpty() {
       
   197         return count == 0;
       
   198     }
       
   199 
       
   200     /**
       
   201      * Returns true if this LinkedHashMap maps one or more keys to the specified
       
   202      * value.
       
   203      *
       
   204      * @param value value whose presence in this Map is to be tested.
       
   205      */
       
   206     public boolean containsValue(Object value) {
       
   207         if (value==null) {
       
   208             for (Entry e = header.after; e != header; e = e.after)
       
   209                 if (e.value==null)
       
   210                     return true;
       
   211         } else {
       
   212             for (Entry e = header.after; e != header; e = e.after)
       
   213                 if (value.equals(e.value))
       
   214                     return true;
       
   215         }
       
   216         return false;
       
   217     }
       
   218 
       
   219     /**
       
   220      * Returns true if this LinkedHashMap contains a mapping for the specified
       
   221      * key.
       
   222      *
       
   223      * @param key key whose presence in this Map is to be tested.
       
   224      */
       
   225     public boolean containsKey(Object key) {
       
   226         Entry tab[] = table;
       
   227         if (key != null) {
       
   228             int hash = key.hashCode();
       
   229             int index = (hash & 0x7FFFFFFF) % tab.length;
       
   230             for (Entry e = tab[index]; e != null; e = e.next)
       
   231                 if (e.hash==hash && e.key.equals(key))
       
   232                     return true;
       
   233         } else {
       
   234             for (Entry e = tab[0]; e != null; e = e.next)
       
   235                 if (e.key==null)
       
   236                     return true;
       
   237         }
       
   238 
       
   239         return false;
       
   240     }
       
   241 
       
   242     /**
       
   243      * Returns the value to which this LinkedHashMap maps the specified key.
       
   244      * Returns null if the LinkedHashMap contains no mapping for this key.
       
   245      * A return value of null does not <em>necessarily</em> indicate that the
       
   246      * LinkedHashMap contains no mapping for the key; it's also possible that
       
   247      * the LinkedHashMap explicitly maps the key to null.  The containsKey
       
   248      * operation may be used to distinguish these two cases.
       
   249      *
       
   250      * @param key key whose associated value is to be returned.
       
   251      */
       
   252     public Object get(Object key) {
       
   253         Entry e = getEntry(key);
       
   254         return e==null ? null : e.value;
       
   255     }
       
   256 
       
   257     /**
       
   258      * Returns the entry associated with the specified key in the LinkedHashMap.
       
   259      * Returns null if the LinkedHashMap contains no mapping for this key.
       
   260      */
       
   261     private Entry getEntry(Object key) {
       
   262         Entry tab[] = table;
       
   263 
       
   264         if (key != null) {
       
   265             int hash = key.hashCode();
       
   266             int index = (hash & 0x7FFFFFFF) % tab.length;
       
   267             for (Entry e = tab[index]; e != null; e = e.next)
       
   268                 if ((e.hash == hash) && e.key.equals(key))
       
   269                     return e;
       
   270         } else {
       
   271             for (Entry e = tab[0]; e != null; e = e.next)
       
   272                 if (e.key==null)
       
   273                     return e;
       
   274         }
       
   275 
       
   276         return null;
       
   277     }
       
   278 
       
   279     /**
       
   280      * Rehashes the contents of the LinkedHashMap into a LinkedHashMap with a
       
   281      * larger capacity. This method is called automatically when the
       
   282      * number of keys in the LinkedHashMap exceeds this LinkedHashMap's capacity
       
   283      * and load factor.
       
   284      */
       
   285     private void rehash() {
       
   286         int oldCapacity = table.length;
       
   287         Entry oldMap[] = table;
       
   288 
       
   289         int newCapacity = oldCapacity * 2 + 1;
       
   290         Entry newMap[] = new Entry[newCapacity];
       
   291 
       
   292         modCount++;
       
   293         threshold = (int)(newCapacity * loadFactor);
       
   294         table = newMap;
       
   295 
       
   296         for (Entry e = header.after; e != header; e = e.after) {
       
   297             int index = (e.hash & 0x7FFFFFFF) % newCapacity;
       
   298             e.next = newMap[index];
       
   299             newMap[index] = e;
       
   300         }
       
   301     }
       
   302 
       
   303     /**
       
   304      * Remove an entry from the linked list.
       
   305      */
       
   306     private void listRemove(Entry entry) {
       
   307         if (entry == null) {
       
   308             return;
       
   309         }
       
   310         entry.before.after = entry.after;
       
   311         entry.after.before = entry.before;
       
   312     }
       
   313 
       
   314    /**
       
   315     * Add the specified entry before the specified existing entry to
       
   316     * the linked list.
       
   317     */
       
   318     private void listAddBefore(Entry entry, Entry existEntry) {
       
   319         entry.after = existEntry;
       
   320         entry.before = existEntry.before;
       
   321         entry.before.after = entry;
       
   322         entry.after.before = entry;
       
   323     }
       
   324 
       
   325     /**
       
   326      * Returns the position of the mapping for the specified key
       
   327      * in the ordered map.
       
   328      *
       
   329      * @param key the specified key.
       
   330      * @return index of the key mapping.
       
   331      */
       
   332     public int indexOf(Object key) {
       
   333         int i = 0;
       
   334         if (key == null) {
       
   335             for (Entry e = header.after; e != header; e = e.after, i++)
       
   336                 if (e.key == null)
       
   337                     return i;
       
   338         } else {
       
   339             for (Entry e = header.after; e != header; e = e.after, i++)
       
   340                 if(key.equals(e.key))
       
   341                     return i;
       
   342         }
       
   343         return -1;
       
   344     }
       
   345 
       
   346     /**
       
   347      * Associates the specified value with the specified key in this
       
   348      * LinkedHashMap. If the LinkedHashMap previously contained a mapping for
       
   349      * this key, the old value is replaced and the position of this mapping
       
   350      * entry in the double linked list remains the same. Otherwise, a new
       
   351      * mapping entry is created and inserted into the list before the specified
       
   352      * existing mapping entry. The method returns the previous value associated
       
   353      * with the specified key, or null if there was no mapping for key.  A null
       
   354      * return can also indicate that the LinkedHashMap previously associated
       
   355      * null with the specified key.
       
   356      */
       
   357     private Object putAhead(Object key, Object value, Entry existEntry) {
       
   358         // Makes sure the key is not already in the LinkedHashMap.
       
   359         Entry tab[] = table;
       
   360         int hash = 0;
       
   361         int index = 0;
       
   362 
       
   363         if (key != null) {
       
   364             hash = key.hashCode();
       
   365             index = (hash & 0x7FFFFFFF) % tab.length;
       
   366             for (Entry e = tab[index] ; e != null ; e = e.next) {
       
   367                 if ((e.hash == hash) && e.key.equals(key)) {
       
   368                     Object old = e.value;
       
   369                     e.value = value;
       
   370                     return old;
       
   371                 }
       
   372             }
       
   373         } else {
       
   374             for (Entry e = tab[0] ; e != null ; e = e.next) {
       
   375                 if (e.key == null) {
       
   376                     Object old = e.value;
       
   377                     e.value = value;
       
   378                     return old;
       
   379                 }
       
   380             }
       
   381         }
       
   382 
       
   383         modCount++;
       
   384         if (count >= threshold) {
       
   385             // Rehash the table if the threshold is exceeded
       
   386             rehash();
       
   387             tab = table;
       
   388             index = (hash & 0x7FFFFFFF) % tab.length;
       
   389         }
       
   390 
       
   391         // Creates the new entry.
       
   392         Entry e = new Entry(hash, key, value, tab[index]);
       
   393         tab[index] = e;
       
   394         listAddBefore(e, existEntry);
       
   395         count++;
       
   396         return null;
       
   397     }
       
   398 
       
   399     /**
       
   400      * Associates the specified value with the specified key in this
       
   401      * LinkedHashMap and position the mapping at the specified index.
       
   402      * If the LinkedHashMap previously contained a mapping for this key,
       
   403      * the old value is replaced and the position of this mapping entry
       
   404      * in the double linked list remains the same. Otherwise, a new mapping
       
   405      * entry is created and inserted into the list at the specified
       
   406      * position.
       
   407      *
       
   408      * @param index     the position to put the key-value mapping.
       
   409      * @param key       key with which the specified value is to be associated.
       
   410      * @param value     value to be associated with the specified key.
       
   411      * @return previous value associated with specified key, or null if there
       
   412      *         was no mapping for key.  A null return can also indicate that
       
   413      *         the LinkedHashMap previously associated null with the specified
       
   414      *         key.
       
   415      */
       
   416     public Object put(int index, Object key, Object value) {
       
   417         if (index < 0 || index > count)
       
   418             throw new IndexOutOfBoundsException();
       
   419         Entry e = header.after;
       
   420         if (index == count)
       
   421             return putAhead(key, value, header); //fast approach for append
       
   422         else {
       
   423             for (int i = 0; i < index; i++)
       
   424                 e = e.after;
       
   425             return putAhead(key, value, e);
       
   426         }
       
   427     }
       
   428 
       
   429 
       
   430     /**
       
   431      * Associates the specified value with the specified key in this
       
   432      * LinkedHashMap. If the LinkedHashMap previously contained a mapping for
       
   433      * this key, the old value is replaced. The mapping entry is also appended
       
   434      * to the end of the ordered linked list.
       
   435      *
       
   436      * @param key key with which the specified value is to be associated.
       
   437      * @param value value to be associated with the specified key.
       
   438      * @return previous value associated with specified key, or null if there
       
   439      *         was no mapping for key.  A null return can also indicate that
       
   440      *         the LinkedHashMap previously associated null with the specified
       
   441      *         key.
       
   442      */
       
   443     public Object put(Object key, Object value) {
       
   444         return putAhead(key, value, header);
       
   445     }
       
   446 
       
   447     /**
       
   448      * Removes the mapping for this key from this LinkedHashMap if present.
       
   449      * The mapping would also be removed from the double linked list.
       
   450      *
       
   451      * @param key key whose mapping is to be removed from the Map.
       
   452      * @return previous value associated with specified key, or null if there
       
   453      *         was no mapping for key.  A null return can also indicate that
       
   454      *         the LinkedHashMap previously associated null with the specified
       
   455      *         key.
       
   456      */
       
   457     public Object remove(Object key) {
       
   458         Entry tab[] = table;
       
   459 
       
   460         if (key != null) {
       
   461             int hash = key.hashCode();
       
   462             int index = (hash & 0x7FFFFFFF) % tab.length;
       
   463 
       
   464             for (Entry e = tab[index], prev = null; e != null;
       
   465                  prev = e, e = e.next) {
       
   466                 if ((e.hash == hash) && e.key.equals(key)) {
       
   467                     modCount++;
       
   468                     if (prev != null)
       
   469                         prev.next = e.next;
       
   470                     else
       
   471                         tab[index] = e.next;
       
   472 
       
   473                     count--;
       
   474                     Object oldValue = e.value;
       
   475                     e.value = null;
       
   476 
       
   477                     listRemove(e);
       
   478                     return oldValue;
       
   479                 }
       
   480             }
       
   481         } else {
       
   482             for (Entry e = tab[0], prev = null; e != null;
       
   483                  prev = e, e = e.next) {
       
   484                 if (e.key == null) {
       
   485                     modCount++;
       
   486                     if (prev != null)
       
   487                         prev.next = e.next;
       
   488                     else
       
   489                         tab[0] = e.next;
       
   490 
       
   491                     count--;
       
   492                     Object oldValue = e.value;
       
   493                     e.value = null;
       
   494 
       
   495                     listRemove(e);
       
   496                     return oldValue;
       
   497                 }
       
   498             }
       
   499         }
       
   500 
       
   501         return null;
       
   502     }
       
   503 
       
   504     /**
       
   505      * Copies all of the mappings from the specified Map to this LinkedHashMap
       
   506      * These mappings will replace any mappings that this LinkedHashMap had for
       
   507      * any of the keys currently in the specified Map.
       
   508      *
       
   509       * @param t Mappings to be stored in this Map.
       
   510      */
       
   511     public void putAll(Map t) {
       
   512         Iterator i = t.entrySet().iterator();
       
   513         while (i.hasNext()) {
       
   514             Map.Entry e = (Map.Entry) i.next();
       
   515             put(e.getKey(), e.getValue());
       
   516         }
       
   517     }
       
   518 
       
   519     /**
       
   520      * Removes all mappings from this LinkedHashMap.
       
   521      */
       
   522     public void clear() {
       
   523         Entry tab[] = table;
       
   524         modCount++;
       
   525         for (int index = tab.length; --index >= 0; )
       
   526             tab[index] = null;
       
   527         count = 0;
       
   528         header.before = header.after = header;
       
   529     }
       
   530 
       
   531     /**
       
   532      * Returns a shallow copy of this LinkedHashMap. The keys and values
       
   533      * themselves are not cloned.
       
   534      */
       
   535     public Object clone() {
       
   536         return new LinkedHashMap(this);
       
   537     }
       
   538 
       
   539     // Views
       
   540 
       
   541     private transient Set keySet = null;
       
   542     private transient Set entries = null;
       
   543     private transient Collection values = null;
       
   544 
       
   545     /**
       
   546      * Returns a Set view of the keys contained in this LinkedHashMap.  The Set
       
   547      * is backed by the LinkedHashMap, so changes to the LinkedHashMap are
       
   548      * reflected in the Set, and vice-versa.  The Set supports element removal,
       
   549      * which removes the corresponding mapping from the LinkedHashMap, via the
       
   550      * Iterator.remove, Set.remove, removeAll retainAll, and clear operations.
       
   551      * It does not support the add or addAll operations.
       
   552      */
       
   553     public Set keySet() {
       
   554         if (keySet == null) {
       
   555             keySet = new AbstractSet() {
       
   556                 public Iterator iterator() {
       
   557                     return new HashIterator(KEYS);
       
   558                 }
       
   559                 public int size() {
       
   560                     return count;
       
   561                 }
       
   562                 public boolean contains(Object o) {
       
   563                     return containsKey(o);
       
   564                 }
       
   565                 public boolean remove(Object o) {
       
   566                     return LinkedHashMap.this.remove(o) != null;
       
   567                 }
       
   568                 public void clear() {
       
   569                     LinkedHashMap.this.clear();
       
   570                 }
       
   571             };
       
   572         }
       
   573         return keySet;
       
   574     }
       
   575 
       
   576     /**
       
   577      * Returns a Collection view of the values contained in this LinkedHashMap.
       
   578      * The Collection is backed by the LinkedHashMap, so changes to the
       
   579      * LinkedHashMap are reflected in the Collection, and vice-versa.  The
       
   580      * Collection supports element removal, which removes the corresponding
       
   581      * mapping from the LinkedHashMap, via the Iterator.remove,
       
   582      * Collection.remove, removeAll, retainAll and clear operations.  It does
       
   583      * not support the add or addAll operations.
       
   584      */
       
   585     public Collection values() {
       
   586         if (values==null) {
       
   587             values = new AbstractCollection() {
       
   588                 public Iterator iterator() {
       
   589                     return new HashIterator(VALUES);
       
   590                 }
       
   591                 public int size() {
       
   592                     return count;
       
   593                 }
       
   594                 public boolean contains(Object o) {
       
   595                     return containsValue(o);
       
   596                 }
       
   597                 public void clear() {
       
   598                     LinkedHashMap.this.clear();
       
   599                 }
       
   600             };
       
   601         }
       
   602         return values;
       
   603     }
       
   604 
       
   605     /**
       
   606      * Returns a Collection view of the mappings contained in this
       
   607      * LinkedHashMap. Each element in the returned collection is a Map.Entry.
       
   608      * The Collection is backed by the LinkedHashMap, so changes to the
       
   609      * LinkedHashMap are reflected in the Collection, and vice-versa.  The
       
   610      * Collection supports element removal, which removes the corresponding
       
   611      * mapping from the LinkedHashMap, via the Iterator.remove,
       
   612      * Collection.remove, removeAll, retainAll and clear operations.  It does
       
   613      * not support the add or addAll operations.
       
   614      *
       
   615      * @see   java.util.Map.Entry
       
   616      */
       
   617     public Set entrySet() {
       
   618         if (entries==null) {
       
   619             entries = new AbstractSet() {
       
   620                 public Iterator iterator() {
       
   621                     return new HashIterator(ENTRIES);
       
   622                 }
       
   623 
       
   624                 public boolean contains(Object o) {
       
   625                     if (!(o instanceof Map.Entry))
       
   626                         return false;
       
   627                     Map.Entry entry = (Map.Entry)o;
       
   628                     Object key = entry.getKey();
       
   629                     Entry tab[] = table;
       
   630                     int hash = (key==null ? 0 : key.hashCode());
       
   631                     int index = (hash & 0x7FFFFFFF) % tab.length;
       
   632 
       
   633                     for (Entry e = tab[index]; e != null; e = e.next)
       
   634                         if (e.hash==hash && e.equals(entry))
       
   635                             return true;
       
   636                     return false;
       
   637                 }
       
   638 
       
   639                 public boolean remove(Object o) {
       
   640                     if (!(o instanceof Map.Entry))
       
   641                         return false;
       
   642                     Map.Entry entry = (Map.Entry)o;
       
   643                     Object key = entry.getKey();
       
   644                     Entry tab[] = table;
       
   645                     int hash = (key==null ? 0 : key.hashCode());
       
   646                     int index = (hash & 0x7FFFFFFF) % tab.length;
       
   647 
       
   648                     for (Entry e = tab[index], prev = null; e != null;
       
   649                          prev = e, e = e.next) {
       
   650                         if (e.hash==hash && e.equals(entry)) {
       
   651                             modCount++;
       
   652                             if (prev != null)
       
   653                                 prev.next = e.next;
       
   654                             else
       
   655                                 tab[index] = e.next;
       
   656 
       
   657                             count--;
       
   658                             e.value = null;
       
   659                             listRemove(e);
       
   660                             return true;
       
   661                         }
       
   662                     }
       
   663                     return false;
       
   664                 }
       
   665 
       
   666                 public int size() {
       
   667                     return count;
       
   668                 }
       
   669 
       
   670                 public void clear() {
       
   671                     LinkedHashMap.this.clear();
       
   672                 }
       
   673             };
       
   674         }
       
   675 
       
   676         return entries;
       
   677     }
       
   678 
       
   679     /**
       
   680      * Compares the specified Object with this Map for equality.
       
   681      * Returns true if the given object is also a LinkedHashMap and the two
       
   682      * Maps represent the same mappings in the same order.  More formally,
       
   683      * two Maps <code>t1</code> and <code>t2</code> represent the same mappings
       
   684      * if <code>t1.keySet().equals(t2.keySet())</code> and for every
       
   685      * key <code>k</code> in <code>t1.keySet()</code>, <code>
       
   686      * (t1.get(k)==null ? t2.get(k)==null : t1.get(k).equals(t2.get(k)))
       
   687      * </code>.
       
   688      * <p>
       
   689      * This implementation first checks if the specified Object is this Map;
       
   690      * if so it returns true.  Then, it checks if the specified Object is
       
   691      * a Map whose size is identical to the size of this Set; if not, it
       
   692      * it returns false.  If so, it iterates over this Map and the specified
       
   693      * Map's entrySet() Collection, and checks that the specified Map contains
       
   694      * each mapping that this Map contains at the same position.  If the
       
   695      * specified Map fails to contain such a mapping in the right order, false
       
   696      * is returned.  If the iteration completes, true is returned.
       
   697      *
       
   698      * @param o Object to be compared for equality with this Map.
       
   699      * @return true if the specified Object is equal to this Map.
       
   700      *
       
   701      */
       
   702     public boolean equals(Object o) {
       
   703         if (o == this)
       
   704             return true;
       
   705 
       
   706         if (!(o instanceof LinkedHashMap))
       
   707             return false;
       
   708         LinkedHashMap t = (LinkedHashMap) o;
       
   709         if (t.size() != size())
       
   710             return false;
       
   711 
       
   712         Iterator i1 = entrySet().iterator();
       
   713         Iterator i2 = t.entrySet().iterator();
       
   714 
       
   715         while (i1.hasNext()) {
       
   716             Entry e1 = (Entry) i1.next();
       
   717             Entry e2 = (Entry) i2.next();
       
   718 
       
   719             Object key1 = e1.getKey();
       
   720             Object value1 = e1.getValue();
       
   721             Object key2 = e2.getKey();
       
   722             Object value2 = e2.getValue();
       
   723 
       
   724             if ((key1 == null ? key2 == null : key1.equals(key2)) &&
       
   725                 (value1 == null ? value2 == null : value1.equals(value2))) {
       
   726                 continue;
       
   727             } else {
       
   728                 return false;
       
   729             }
       
   730         }
       
   731         return true;
       
   732     }
       
   733 
       
   734     /**
       
   735      * LinkedHashMap collision list entry.
       
   736      */
       
   737     private static class Entry implements Map.Entry {
       
   738         int hash;
       
   739         Object key;
       
   740         Object value;
       
   741         Entry next;
       
   742 
       
   743         // These fields comprise the doubly linked list that is used for
       
   744         // iteration.
       
   745         Entry before, after;
       
   746 
       
   747         Entry(int hash, Object key, Object value, Entry next) {
       
   748             this.hash = hash;
       
   749             this.key = key;
       
   750             this.value = value;
       
   751             this.next = next;
       
   752         }
       
   753 
       
   754         // Map.Entry Ops
       
   755 
       
   756         public Object getKey() {
       
   757             return key;
       
   758         }
       
   759 
       
   760         public Object getValue() {
       
   761             return value;
       
   762         }
       
   763 
       
   764         public Object setValue(Object value) {
       
   765             Object oldValue = this.value;
       
   766             this.value = value;
       
   767             return oldValue;
       
   768         }
       
   769 
       
   770         public boolean equals(Object o) {
       
   771             if (!(o instanceof Map.Entry))
       
   772                 return false;
       
   773             Map.Entry e = (Map.Entry)o;
       
   774 
       
   775             return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
       
   776                (value==null ? e.getValue()==null : value.equals(e.getValue()));
       
   777         }
       
   778 
       
   779         public int hashCode() {
       
   780             return hash ^ (value==null ? 0 : value.hashCode());
       
   781         }
       
   782 
       
   783         public String toString() {
       
   784             return key+"="+value;
       
   785         }
       
   786     }
       
   787 
       
   788     // Types of Iterators
       
   789     private static final int KEYS = 0;
       
   790     private static final int VALUES = 1;
       
   791     private static final int ENTRIES = 2;
       
   792 
       
   793     private class HashIterator implements Iterator {
       
   794         private Entry[] table = LinkedHashMap.this.table;
       
   795         private Entry entry = null;
       
   796         private Entry lastReturned = null;
       
   797         private int type;
       
   798 
       
   799         /**
       
   800          * The modCount value that the iterator believes that the backing
       
   801          * List should have.  If this expectation is violated, the iterator
       
   802          * has detected concurrent modification.
       
   803          */
       
   804         private int expectedModCount = modCount;
       
   805 
       
   806         HashIterator(int type) {
       
   807             this.type = type;
       
   808             this.entry = LinkedHashMap.this.header.after;
       
   809         }
       
   810 
       
   811         public boolean hasNext() {
       
   812             return entry != header;
       
   813         }
       
   814 
       
   815         public Object next() {
       
   816             if (modCount != expectedModCount)
       
   817                 throw new ConcurrentModificationException();
       
   818             if (entry == LinkedHashMap.this.header)
       
   819                 throw new NoSuchElementException();
       
   820 
       
   821             Entry e = lastReturned = entry;
       
   822             entry = e.after;
       
   823             return type == KEYS ? e.key : (type == VALUES ? e.value : e);
       
   824         }
       
   825 
       
   826         public void remove() {
       
   827             if (lastReturned == null)
       
   828                 throw new IllegalStateException();
       
   829             if (modCount != expectedModCount)
       
   830                 throw new ConcurrentModificationException();
       
   831 
       
   832             Entry[] tab = LinkedHashMap.this.table;
       
   833             int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
       
   834 
       
   835             for (Entry e = tab[index], prev = null; e != null;
       
   836                  prev = e, e = e.next) {
       
   837                 if (e == lastReturned) {
       
   838                     modCount++;
       
   839                     expectedModCount++;
       
   840                     if (prev == null)
       
   841                         tab[index] = e.next;
       
   842                     else
       
   843                         prev.next = e.next;
       
   844                     count--;
       
   845                     listRemove(e);
       
   846                     lastReturned = null;
       
   847                     return;
       
   848                 }
       
   849             }
       
   850             throw new ConcurrentModificationException();
       
   851         }
       
   852     }
       
   853 
       
   854     /**
       
   855      * Save the state of the LinkedHashMap to a stream (i.e., serialize it).
       
   856      * The objects will be written out in the order they are linked
       
   857      * in the list.
       
   858      */
       
   859     private void writeObject(java.io.ObjectOutputStream s)
       
   860         throws IOException
       
   861     {
       
   862         // Write out the threshold, loadfactor, and any hidden stuff
       
   863         s.defaultWriteObject();
       
   864 
       
   865         // Write out number of buckets
       
   866         s.writeInt(table.length);
       
   867 
       
   868         // Write out size (number of Mappings)
       
   869         s.writeInt(count);
       
   870 
       
   871         // Write out keys and values (alternating)
       
   872         for (Entry e = header.after; e != header; e = e.after) {
       
   873             s.writeObject(e.key);
       
   874             s.writeObject(e.value);
       
   875         }
       
   876     }
       
   877 
       
   878     /**
       
   879      * Reconstitute the LinkedHashMap from a stream (i.e., deserialize it).
       
   880      */
       
   881     private void readObject(java.io.ObjectInputStream s)
       
   882          throws IOException, ClassNotFoundException
       
   883     {
       
   884         // Read in the threshold, loadfactor, and any hidden stuff
       
   885         s.defaultReadObject();
       
   886 
       
   887         // Read in number of buckets and allocate the bucket array;
       
   888         int numBuckets = s.readInt();
       
   889         table = new Entry[numBuckets];
       
   890         header = new Entry(-1, null, null, null);
       
   891         header.before = header;
       
   892         header.after = header;
       
   893 
       
   894         // Read in size (number of Mappings)
       
   895         int size = s.readInt();
       
   896 
       
   897         // Read the keys and values, and put the mappings in the LinkedHashMap
       
   898         for (int i=0; i<size; i++) {
       
   899             Object key = s.readObject();
       
   900             Object value = s.readObject();
       
   901             put(key, value);
       
   902         }
       
   903     }
       
   904 }