| /* |
| * Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package com.sun.tools.jdi; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * Hash table based implementation of the Map interface. This implementation |
| * provides all of the optional Map operations, and permits null values and |
| * the null key. (HashMap is roughly equivalent to Hashtable, except that it |
| * is unsynchronized and permits nulls.) In addition, elements in the map are |
| * ordered and doubly linked together. |
| * <p> |
| * This implementation provides constant-time performance for the basic |
| * operations (get and put), assuming the the hash function disperses the |
| * elements properly among the buckets. Iteration over Collection views |
| * requires time proportional to its size (the number of key-value mappings) |
| * and returns elements in the order they are linked. In a HashMap the |
| * iteration would require time proportional to the capacity of the map |
| * plus the map size. |
| * <p> |
| * An instance of LinkedHashMap has two parameters that affect its efficiency: |
| * its <i>capacity</i> and its <i>load factor</i>. The load factor should be |
| * between 0.0 and 1.0. When the number of mappings in the LinkedHashMap exceeds |
| * the product of the load factor and the current capacity, the capacity is |
| * increased by calling the rehash method which requires time proportional |
| * to the number of key-value mappings in the map. Larger load factors |
| * use memory more efficiently, at the expense of larger expected time per |
| * lookup. |
| * <p> |
| * If many mappings are to be stored in a LinkedHashMap, creating it with a |
| * sufficiently large capacity will allow the mappings to be stored more |
| * efficiently than letting it perform automatic rehashing as needed to grow |
| * the table. |
| * <p> |
| * <strong>Note that this implementation is not synchronized.</strong> If |
| * multiple threads access a LinkedHashMap concurrently, and at least one of the |
| * threads modifies the LinkedHashMap structurally, it <em>must</em> be |
| * synchronized externally. (A structural modification is any operation that |
| * adds or deletes one or more mappings; merely changing the value associated |
| * with a key that is already contained in the Table is not a structural |
| * modification.) This is typically accomplished by synchronizing on some |
| * object that naturally encapsulates the LinkedHashMap. If no such object |
| * exists, the LinkedHashMap should be "wrapped" using the |
| * Collections.synchronizedSet method. This is best done at creation time, to |
| * prevent accidental unsynchronized access to the LinkedHashMap: |
| * <pre> |
| * Map m = Collections.synchronizedMap(new LinkedHashMap(...)); |
| * </pre> |
| * <p> |
| * The Iterators returned by the iterator methods of the Collections returned |
| * by all of LinkedHashMap's "collection view methods" are <em>fail-fast</em>: |
| * if the LinkedHashMap is structurally modified at any time after the Iterator |
| * is created, in any way except through the Iterator's own remove or add |
| * methods, the Iterator will throw a ConcurrentModificationException. Thus, |
| * in the face of concurrent modification, the Iterator fails quickly and |
| * cleanly, rather than risking arbitrary, non-deterministic behavior at an |
| * undetermined time in the future. |
| * |
| * @author Josh Bloch |
| * @author Arthur van Hoff |
| * @author Zhenghua Li |
| * @see Object#hashCode() |
| * @see java.util.Collection |
| * @see java.util.Map |
| * @see java.util.TreeMap |
| * @see java.util.Hashtable |
| * @see java.util.HashMap |
| */ |
| |
| import java.io.Serializable; |
| |
| public class LinkedHashMap extends AbstractMap implements Map, Serializable { |
| /** |
| * The hash table data. |
| */ |
| private transient Entry table[]; |
| |
| /** |
| * The head of the double linked list. |
| */ |
| private transient Entry header; |
| |
| /** |
| * The total number of mappings in the hash table. |
| */ |
| private transient int count; |
| |
| /** |
| * Rehashes the table when count exceeds this threshold. |
| */ |
| private int threshold; |
| |
| /** |
| * The load factor for the LinkedHashMap. |
| */ |
| private float loadFactor; |
| |
| /** |
| * The number of times this LinkedHashMap has been structurally modified |
| * Structural modifications are those that change the number of mappings in |
| * the LinkedHashMap or otherwise modify its internal structure (e.g., |
| * rehash). This field is used to make iterators on Collection-views of |
| * the LinkedHashMap fail-fast. (See ConcurrentModificationException). |
| */ |
| private transient int modCount = 0; |
| |
| /** |
| * Constructs a new, empty LinkedHashMap with the specified initial |
| * capacity and the specified load factor. |
| * |
| * @param initialCapacity the initial capacity of the LinkedHashMap. |
| * @param loadFactor a number between 0.0 and 1.0. |
| * @exception IllegalArgumentException if the initial capacity is less |
| * than or equal to zero, or if the load factor is less than |
| * or equal to zero. |
| */ |
| public LinkedHashMap(int initialCapacity, float loadFactor) { |
| if (initialCapacity < 0) |
| throw new IllegalArgumentException("Illegal Initial Capacity: "+ |
| initialCapacity); |
| if ((loadFactor > 1) || (loadFactor <= 0)) |
| throw new IllegalArgumentException("Illegal Load factor: "+ |
| loadFactor); |
| if (initialCapacity==0) |
| initialCapacity = 1; |
| this.loadFactor = loadFactor; |
| table = new Entry[initialCapacity]; |
| threshold = (int)(initialCapacity * loadFactor); |
| header = new Entry(-1, null, null, null); |
| header.before = header.after = header; |
| } |
| |
| /** |
| * Constructs a new, empty LinkedHashMap with the specified initial capacity |
| * and default load factor. |
| * |
| * @param initialCapacity the initial capacity of the LinkedHashMap. |
| */ |
| public LinkedHashMap(int initialCapacity) { |
| this(initialCapacity, 0.75f); |
| } |
| |
| /** |
| * Constructs a new, empty LinkedHashMap with a default capacity and load |
| * factor. |
| */ |
| public LinkedHashMap() { |
| this(101, 0.75f); |
| } |
| |
| /** |
| * Constructs a new LinkedHashMap with the same mappings as the given |
| * Map. The LinkedHashMap is created with a capacity of thrice the number |
| * of mappings in the given Map or 11 (whichever is greater), and a |
| * default load factor. |
| */ |
| public LinkedHashMap(Map t) { |
| this(Math.max(3*t.size(), 11), 0.75f); |
| putAll(t); |
| } |
| |
| /** |
| * Returns the number of key-value mappings in this Map. |
| */ |
| public int size() { |
| return count; |
| } |
| |
| /** |
| * Returns true if this Map contains no key-value mappings. |
| */ |
| public boolean isEmpty() { |
| return count == 0; |
| } |
| |
| /** |
| * Returns true if this LinkedHashMap maps one or more keys to the specified |
| * value. |
| * |
| * @param value value whose presence in this Map is to be tested. |
| */ |
| public boolean containsValue(Object value) { |
| if (value==null) { |
| for (Entry e = header.after; e != header; e = e.after) |
| if (e.value==null) |
| return true; |
| } else { |
| for (Entry e = header.after; e != header; e = e.after) |
| if (value.equals(e.value)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns true if this LinkedHashMap contains a mapping for the specified |
| * key. |
| * |
| * @param key key whose presence in this Map is to be tested. |
| */ |
| public boolean containsKey(Object key) { |
| Entry tab[] = table; |
| if (key != null) { |
| int hash = key.hashCode(); |
| int index = (hash & 0x7FFFFFFF) % tab.length; |
| for (Entry e = tab[index]; e != null; e = e.next) |
| if (e.hash==hash && e.key.equals(key)) |
| return true; |
| } else { |
| for (Entry e = tab[0]; e != null; e = e.next) |
| if (e.key==null) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns the value to which this LinkedHashMap maps the specified key. |
| * Returns null if the LinkedHashMap contains no mapping for this key. |
| * A return value of null does not <em>necessarily</em> indicate that the |
| * LinkedHashMap contains no mapping for the key; it's also possible that |
| * the LinkedHashMap explicitly maps the key to null. The containsKey |
| * operation may be used to distinguish these two cases. |
| * |
| * @param key key whose associated value is to be returned. |
| */ |
| public Object get(Object key) { |
| Entry e = getEntry(key); |
| return e==null ? null : e.value; |
| } |
| |
| /** |
| * Returns the entry associated with the specified key in the LinkedHashMap. |
| * Returns null if the LinkedHashMap contains no mapping for this key. |
| */ |
| private Entry getEntry(Object key) { |
| Entry tab[] = table; |
| |
| if (key != null) { |
| int hash = key.hashCode(); |
| int index = (hash & 0x7FFFFFFF) % tab.length; |
| for (Entry e = tab[index]; e != null; e = e.next) |
| if ((e.hash == hash) && e.key.equals(key)) |
| return e; |
| } else { |
| for (Entry e = tab[0]; e != null; e = e.next) |
| if (e.key==null) |
| return e; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Rehashes the contents of the LinkedHashMap into a LinkedHashMap with a |
| * larger capacity. This method is called automatically when the |
| * number of keys in the LinkedHashMap exceeds this LinkedHashMap's capacity |
| * and load factor. |
| */ |
| private void rehash() { |
| int oldCapacity = table.length; |
| Entry oldMap[] = table; |
| |
| int newCapacity = oldCapacity * 2 + 1; |
| Entry newMap[] = new Entry[newCapacity]; |
| |
| modCount++; |
| threshold = (int)(newCapacity * loadFactor); |
| table = newMap; |
| |
| for (Entry e = header.after; e != header; e = e.after) { |
| int index = (e.hash & 0x7FFFFFFF) % newCapacity; |
| e.next = newMap[index]; |
| newMap[index] = e; |
| } |
| } |
| |
| /** |
| * Remove an entry from the linked list. |
| */ |
| private void listRemove(Entry entry) { |
| if (entry == null) { |
| return; |
| } |
| entry.before.after = entry.after; |
| entry.after.before = entry.before; |
| } |
| |
| /** |
| * Add the specified entry before the specified existing entry to |
| * the linked list. |
| */ |
| private void listAddBefore(Entry entry, Entry existEntry) { |
| entry.after = existEntry; |
| entry.before = existEntry.before; |
| entry.before.after = entry; |
| entry.after.before = entry; |
| } |
| |
| /** |
| * Returns the position of the mapping for the specified key |
| * in the ordered map. |
| * |
| * @param key the specified key. |
| * @return index of the key mapping. |
| */ |
| public int indexOf(Object key) { |
| int i = 0; |
| if (key == null) { |
| for (Entry e = header.after; e != header; e = e.after, i++) |
| if (e.key == null) |
| return i; |
| } else { |
| for (Entry e = header.after; e != header; e = e.after, i++) |
| if(key.equals(e.key)) |
| return i; |
| } |
| return -1; |
| } |
| |
| /** |
| * Associates the specified value with the specified key in this |
| * LinkedHashMap. If the LinkedHashMap previously contained a mapping for |
| * this key, the old value is replaced and the position of this mapping |
| * entry in the double linked list remains the same. Otherwise, a new |
| * mapping entry is created and inserted into the list before the specified |
| * existing mapping entry. The method returns the previous value associated |
| * with the specified key, or null if there was no mapping for key. A null |
| * return can also indicate that the LinkedHashMap previously associated |
| * null with the specified key. |
| */ |
| private Object putAhead(Object key, Object value, Entry existEntry) { |
| // Makes sure the key is not already in the LinkedHashMap. |
| Entry tab[] = table; |
| int hash = 0; |
| int index = 0; |
| |
| if (key != null) { |
| hash = key.hashCode(); |
| index = (hash & 0x7FFFFFFF) % tab.length; |
| for (Entry e = tab[index] ; e != null ; e = e.next) { |
| if ((e.hash == hash) && e.key.equals(key)) { |
| Object old = e.value; |
| e.value = value; |
| return old; |
| } |
| } |
| } else { |
| for (Entry e = tab[0] ; e != null ; e = e.next) { |
| if (e.key == null) { |
| Object old = e.value; |
| e.value = value; |
| return old; |
| } |
| } |
| } |
| |
| modCount++; |
| if (count >= threshold) { |
| // Rehash the table if the threshold is exceeded |
| rehash(); |
| tab = table; |
| index = (hash & 0x7FFFFFFF) % tab.length; |
| } |
| |
| // Creates the new entry. |
| Entry e = new Entry(hash, key, value, tab[index]); |
| tab[index] = e; |
| listAddBefore(e, existEntry); |
| count++; |
| return null; |
| } |
| |
| /** |
| * Associates the specified value with the specified key in this |
| * LinkedHashMap and position the mapping at the specified index. |
| * If the LinkedHashMap previously contained a mapping for this key, |
| * the old value is replaced and the position of this mapping entry |
| * in the double linked list remains the same. Otherwise, a new mapping |
| * entry is created and inserted into the list at the specified |
| * position. |
| * |
| * @param index the position to put the key-value mapping. |
| * @param key key with which the specified value is to be associated. |
| * @param value value to be associated with the specified key. |
| * @return previous value associated with specified key, or null if there |
| * was no mapping for key. A null return can also indicate that |
| * the LinkedHashMap previously associated null with the specified |
| * key. |
| */ |
| public Object put(int index, Object key, Object value) { |
| if (index < 0 || index > count) |
| throw new IndexOutOfBoundsException(); |
| Entry e = header.after; |
| if (index == count) |
| return putAhead(key, value, header); //fast approach for append |
| else { |
| for (int i = 0; i < index; i++) |
| e = e.after; |
| return putAhead(key, value, e); |
| } |
| } |
| |
| |
| /** |
| * Associates the specified value with the specified key in this |
| * LinkedHashMap. If the LinkedHashMap previously contained a mapping for |
| * this key, the old value is replaced. The mapping entry is also appended |
| * to the end of the ordered linked list. |
| * |
| * @param key key with which the specified value is to be associated. |
| * @param value value to be associated with the specified key. |
| * @return previous value associated with specified key, or null if there |
| * was no mapping for key. A null return can also indicate that |
| * the LinkedHashMap previously associated null with the specified |
| * key. |
| */ |
| public Object put(Object key, Object value) { |
| return putAhead(key, value, header); |
| } |
| |
| /** |
| * Removes the mapping for this key from this LinkedHashMap if present. |
| * The mapping would also be removed from the double linked list. |
| * |
| * @param key key whose mapping is to be removed from the Map. |
| * @return previous value associated with specified key, or null if there |
| * was no mapping for key. A null return can also indicate that |
| * the LinkedHashMap previously associated null with the specified |
| * key. |
| */ |
| public Object remove(Object key) { |
| Entry tab[] = table; |
| |
| if (key != null) { |
| int hash = key.hashCode(); |
| int index = (hash & 0x7FFFFFFF) % tab.length; |
| |
| for (Entry e = tab[index], prev = null; e != null; |
| prev = e, e = e.next) { |
| if ((e.hash == hash) && e.key.equals(key)) { |
| modCount++; |
| if (prev != null) |
| prev.next = e.next; |
| else |
| tab[index] = e.next; |
| |
| count--; |
| Object oldValue = e.value; |
| e.value = null; |
| |
| listRemove(e); |
| return oldValue; |
| } |
| } |
| } else { |
| for (Entry e = tab[0], prev = null; e != null; |
| prev = e, e = e.next) { |
| if (e.key == null) { |
| modCount++; |
| if (prev != null) |
| prev.next = e.next; |
| else |
| tab[0] = e.next; |
| |
| count--; |
| Object oldValue = e.value; |
| e.value = null; |
| |
| listRemove(e); |
| return oldValue; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Copies all of the mappings from the specified Map to this LinkedHashMap |
| * These mappings will replace any mappings that this LinkedHashMap had for |
| * any of the keys currently in the specified Map. |
| * |
| * @param t Mappings to be stored in this Map. |
| */ |
| public void putAll(Map t) { |
| Iterator i = t.entrySet().iterator(); |
| while (i.hasNext()) { |
| Map.Entry e = (Map.Entry) i.next(); |
| put(e.getKey(), e.getValue()); |
| } |
| } |
| |
| /** |
| * Removes all mappings from this LinkedHashMap. |
| */ |
| public void clear() { |
| Entry tab[] = table; |
| modCount++; |
| for (int index = tab.length; --index >= 0; ) |
| tab[index] = null; |
| count = 0; |
| header.before = header.after = header; |
| } |
| |
| /** |
| * Returns a shallow copy of this LinkedHashMap. The keys and values |
| * themselves are not cloned. |
| */ |
| public Object clone() { |
| return new LinkedHashMap(this); |
| } |
| |
| // Views |
| |
| private transient Set keySet = null; |
| private transient Set entries = null; |
| private transient Collection values = null; |
| |
| /** |
| * Returns a Set view of the keys contained in this LinkedHashMap. The Set |
| * is backed by the LinkedHashMap, so changes to the LinkedHashMap are |
| * reflected in the Set, and vice-versa. The Set supports element removal, |
| * which removes the corresponding mapping from the LinkedHashMap, via the |
| * Iterator.remove, Set.remove, removeAll retainAll, and clear operations. |
| * It does not support the add or addAll operations. |
| */ |
| public Set keySet() { |
| if (keySet == null) { |
| keySet = new AbstractSet() { |
| public Iterator iterator() { |
| return new HashIterator(KEYS); |
| } |
| public int size() { |
| return count; |
| } |
| public boolean contains(Object o) { |
| return containsKey(o); |
| } |
| public boolean remove(Object o) { |
| return LinkedHashMap.this.remove(o) != null; |
| } |
| public void clear() { |
| LinkedHashMap.this.clear(); |
| } |
| }; |
| } |
| return keySet; |
| } |
| |
| /** |
| * Returns a Collection view of the values contained in this LinkedHashMap. |
| * The Collection is backed by the LinkedHashMap, so changes to the |
| * LinkedHashMap are reflected in the Collection, and vice-versa. The |
| * Collection supports element removal, which removes the corresponding |
| * mapping from the LinkedHashMap, via the Iterator.remove, |
| * Collection.remove, removeAll, retainAll and clear operations. It does |
| * not support the add or addAll operations. |
| */ |
| public Collection values() { |
| if (values==null) { |
| values = new AbstractCollection() { |
| public Iterator iterator() { |
| return new HashIterator(VALUES); |
| } |
| public int size() { |
| return count; |
| } |
| public boolean contains(Object o) { |
| return containsValue(o); |
| } |
| public void clear() { |
| LinkedHashMap.this.clear(); |
| } |
| }; |
| } |
| return values; |
| } |
| |
| /** |
| * Returns a Collection view of the mappings contained in this |
| * LinkedHashMap. Each element in the returned collection is a Map.Entry. |
| * The Collection is backed by the LinkedHashMap, so changes to the |
| * LinkedHashMap are reflected in the Collection, and vice-versa. The |
| * Collection supports element removal, which removes the corresponding |
| * mapping from the LinkedHashMap, via the Iterator.remove, |
| * Collection.remove, removeAll, retainAll and clear operations. It does |
| * not support the add or addAll operations. |
| * |
| * @see java.util.Map.Entry |
| */ |
| public Set entrySet() { |
| if (entries==null) { |
| entries = new AbstractSet() { |
| public Iterator iterator() { |
| return new HashIterator(ENTRIES); |
| } |
| |
| public boolean contains(Object o) { |
| if (!(o instanceof Map.Entry)) |
| return false; |
| Map.Entry entry = (Map.Entry)o; |
| Object key = entry.getKey(); |
| Entry tab[] = table; |
| int hash = (key==null ? 0 : key.hashCode()); |
| int index = (hash & 0x7FFFFFFF) % tab.length; |
| |
| for (Entry e = tab[index]; e != null; e = e.next) |
| if (e.hash==hash && e.equals(entry)) |
| return true; |
| return false; |
| } |
| |
| public boolean remove(Object o) { |
| if (!(o instanceof Map.Entry)) |
| return false; |
| Map.Entry entry = (Map.Entry)o; |
| Object key = entry.getKey(); |
| Entry tab[] = table; |
| int hash = (key==null ? 0 : key.hashCode()); |
| int index = (hash & 0x7FFFFFFF) % tab.length; |
| |
| for (Entry e = tab[index], prev = null; e != null; |
| prev = e, e = e.next) { |
| if (e.hash==hash && e.equals(entry)) { |
| modCount++; |
| if (prev != null) |
| prev.next = e.next; |
| else |
| tab[index] = e.next; |
| |
| count--; |
| e.value = null; |
| listRemove(e); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public int size() { |
| return count; |
| } |
| |
| public void clear() { |
| LinkedHashMap.this.clear(); |
| } |
| }; |
| } |
| |
| return entries; |
| } |
| |
| /** |
| * Compares the specified Object with this Map for equality. |
| * Returns true if the given object is also a LinkedHashMap and the two |
| * Maps represent the same mappings in the same order. More formally, |
| * two Maps <code>t1</code> and <code>t2</code> represent the same mappings |
| * if <code>t1.keySet().equals(t2.keySet())</code> and for every |
| * key <code>k</code> in <code>t1.keySet()</code>, <code> |
| * (t1.get(k)==null ? t2.get(k)==null : t1.get(k).equals(t2.get(k))) |
| * </code>. |
| * <p> |
| * This implementation first checks if the specified Object is this Map; |
| * if so it returns true. Then, it checks if the specified Object is |
| * a Map whose size is identical to the size of this Set; if not, it |
| * it returns false. If so, it iterates over this Map and the specified |
| * Map's entrySet() Collection, and checks that the specified Map contains |
| * each mapping that this Map contains at the same position. If the |
| * specified Map fails to contain such a mapping in the right order, false |
| * is returned. If the iteration completes, true is returned. |
| * |
| * @param o Object to be compared for equality with this Map. |
| * @return true if the specified Object is equal to this Map. |
| * |
| */ |
| public boolean equals(Object o) { |
| if (o == this) |
| return true; |
| |
| if (!(o instanceof LinkedHashMap)) |
| return false; |
| LinkedHashMap t = (LinkedHashMap) o; |
| if (t.size() != size()) |
| return false; |
| |
| Iterator i1 = entrySet().iterator(); |
| Iterator i2 = t.entrySet().iterator(); |
| |
| while (i1.hasNext()) { |
| Entry e1 = (Entry) i1.next(); |
| Entry e2 = (Entry) i2.next(); |
| |
| Object key1 = e1.getKey(); |
| Object value1 = e1.getValue(); |
| Object key2 = e2.getKey(); |
| Object value2 = e2.getValue(); |
| |
| if ((key1 == null ? key2 == null : key1.equals(key2)) && |
| (value1 == null ? value2 == null : value1.equals(value2))) { |
| continue; |
| } else { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * LinkedHashMap collision list entry. |
| */ |
| private static class Entry implements Map.Entry { |
| int hash; |
| Object key; |
| Object value; |
| Entry next; |
| |
| // These fields comprise the doubly linked list that is used for |
| // iteration. |
| Entry before, after; |
| |
| Entry(int hash, Object key, Object value, Entry next) { |
| this.hash = hash; |
| this.key = key; |
| this.value = value; |
| this.next = next; |
| } |
| |
| // Map.Entry Ops |
| |
| public Object getKey() { |
| return key; |
| } |
| |
| public Object getValue() { |
| return value; |
| } |
| |
| public Object setValue(Object value) { |
| Object oldValue = this.value; |
| this.value = value; |
| return oldValue; |
| } |
| |
| public boolean equals(Object o) { |
| if (!(o instanceof Map.Entry)) |
| return false; |
| Map.Entry e = (Map.Entry)o; |
| |
| return (key==null ? e.getKey()==null : key.equals(e.getKey())) && |
| (value==null ? e.getValue()==null : value.equals(e.getValue())); |
| } |
| |
| public int hashCode() { |
| return hash ^ (value==null ? 0 : value.hashCode()); |
| } |
| |
| public String toString() { |
| return key+"="+value; |
| } |
| } |
| |
| // Types of Iterators |
| private static final int KEYS = 0; |
| private static final int VALUES = 1; |
| private static final int ENTRIES = 2; |
| |
| private class HashIterator implements Iterator { |
| private Entry[] table = LinkedHashMap.this.table; |
| private Entry entry = null; |
| private Entry lastReturned = null; |
| private int type; |
| |
| /** |
| * The modCount value that the iterator believes that the backing |
| * List should have. If this expectation is violated, the iterator |
| * has detected concurrent modification. |
| */ |
| private int expectedModCount = modCount; |
| |
| HashIterator(int type) { |
| this.type = type; |
| this.entry = LinkedHashMap.this.header.after; |
| } |
| |
| public boolean hasNext() { |
| return entry != header; |
| } |
| |
| public Object next() { |
| if (modCount != expectedModCount) |
| throw new ConcurrentModificationException(); |
| if (entry == LinkedHashMap.this.header) |
| throw new NoSuchElementException(); |
| |
| Entry e = lastReturned = entry; |
| entry = e.after; |
| return type == KEYS ? e.key : (type == VALUES ? e.value : e); |
| } |
| |
| public void remove() { |
| if (lastReturned == null) |
| throw new IllegalStateException(); |
| if (modCount != expectedModCount) |
| throw new ConcurrentModificationException(); |
| |
| Entry[] tab = LinkedHashMap.this.table; |
| int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; |
| |
| for (Entry e = tab[index], prev = null; e != null; |
| prev = e, e = e.next) { |
| if (e == lastReturned) { |
| modCount++; |
| expectedModCount++; |
| if (prev == null) |
| tab[index] = e.next; |
| else |
| prev.next = e.next; |
| count--; |
| listRemove(e); |
| lastReturned = null; |
| return; |
| } |
| } |
| throw new ConcurrentModificationException(); |
| } |
| } |
| |
| /** |
| * Save the state of the LinkedHashMap to a stream (i.e., serialize it). |
| * The objects will be written out in the order they are linked |
| * in the list. |
| */ |
| private void writeObject(java.io.ObjectOutputStream s) |
| throws IOException |
| { |
| // Write out the threshold, loadfactor, and any hidden stuff |
| s.defaultWriteObject(); |
| |
| // Write out number of buckets |
| s.writeInt(table.length); |
| |
| // Write out size (number of Mappings) |
| s.writeInt(count); |
| |
| // Write out keys and values (alternating) |
| for (Entry e = header.after; e != header; e = e.after) { |
| s.writeObject(e.key); |
| s.writeObject(e.value); |
| } |
| } |
| |
| /** |
| * Reconstitute the LinkedHashMap from a stream (i.e., deserialize it). |
| */ |
| private void readObject(java.io.ObjectInputStream s) |
| throws IOException, ClassNotFoundException |
| { |
| // Read in the threshold, loadfactor, and any hidden stuff |
| s.defaultReadObject(); |
| |
| // Read in number of buckets and allocate the bucket array; |
| int numBuckets = s.readInt(); |
| table = new Entry[numBuckets]; |
| header = new Entry(-1, null, null, null); |
| header.before = header; |
| header.after = header; |
| |
| // Read in size (number of Mappings) |
| int size = s.readInt(); |
| |
| // Read the keys and values, and put the mappings in the LinkedHashMap |
| for (int i=0; i<size; i++) { |
| Object key = s.readObject(); |
| Object value = s.readObject(); |
| put(key, value); |
| } |
| } |
| } |