Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Sequenced HashMap

/*  * $Header: /home/projects/aspectwerkz/scm/aspectwerkz4/src/main/org/codehaus/aspectwerkz/util/SequencedHashMap.java,v 1.3 2004/10/22 12:40:40 avasseur Exp $  * $Revision: 1.3 $  * $Date: 2004/10/22 12:40:40 $  *  *   *  * The Apache Software License, Version 1.1  *  * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights  * reserved.  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  *  * 1. Redistributions of source code must retain the above copyright  *    notice, this list of conditions and the following disclaimer.  *  * 2. Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in  *    the documentation and/or other materials provided with the  *    distribution.  *  * 3. The end-user documentation included with the redistribution, if  *    any, must include the following acknowlegement:  *       "This product includes software developed by the  *        Apache Software Foundation (http://www.apache.org/)."  *    Alternately, this acknowlegement may appear in the software itself,  *    if and wherever such third-party acknowlegements normally appear.  *  * 4. The names "The Jakarta Project", "Commons", and "Apache Software  *    Foundation" must not be used to endorse or promote products derived  *    from this software without prior written permission. For written  *    permission, please contact apache@apache.org.  *  * 5. Products derived from this software may not be called "Apache"  *    nor may "Apache" appear in their names without prior written  *    permission of the Apache Group.  *  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  * SUCH DAMAGE.  *   *  * This software consists of voluntary contributions made by many  * individuals on behalf of the Apache Software Foundation.  For more  * information on the Apache Software Foundation, please see  * <http://www.apache.org/>.  *  */ import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /**  * A map of objects whose mapping entries are sequenced based on the order in which they were added. This data structure  * has fast <I>O(1) </I> search time, deletion time, and insertion time. <p/>  * <P>  * Although this map is sequenced, it cannot implement {@link java.util.List}because of incompatible interface  * definitions. The remove methods in List and Map have different return values (see:  * {@linkjava.util.List#remove(Object)}and {@link java.util.Map#remove(Object)}).<p/>  * <P>  * This class is not thread safe. When a thread safe implementation is required, use {@link  * Collections#synchronizedMap(Map)} as it is documented, or use explicit synchronization controls.  *  * @author <a href="mailto:mas@apache.org">Michael A. Smith </A>  * @author <a href="mailto:dlr@collab.net">Daniel Rall </a>  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen </a>  * @since 2.0  */ public class SequencedHashMap implements Map, Cloneable, Externalizable {     // constants to define what the iterator should return on "next"     private static final int KEY = 0;     private static final int VALUE = 1;     private static final int ENTRY = 2;     private static final int REMOVED_MASK = 0x80000000;     // add a serial version uid, so that if we change things in the future     // without changing the format, we can still deserialize properly.     private static final long serialVersionUID = 3380552487888102930L;     /**      * Sentinel used to hold the head and tail of the list of entries.      */     private Entry sentinel;     /**      * Map of keys to entries      */     private HashMap entries;     /**      * Holds the number of modifications that have occurred to the map, excluding modifications made through a      * collection view's iterator (e.g. entrySet().iterator().remove()). This is used to create a fail-fast behavior      * with the iterators.      */     private transient long modCount = 0;     /**      * Construct a new sequenced hash map with default initial size and load factor.      */     public SequencedHashMap() {         sentinel = createSentinel();         entries = new HashMap();     }     /**      * Construct a new sequenced hash map with the specified initial size and default load factor.      *      * @param initialSize the initial size for the hash table      * @see HashMap#HashMap(int)      */     public SequencedHashMap(int initialSize) {         sentinel = createSentinel();         entries = new HashMap(initialSize);     }     /**      * Construct a new sequenced hash map with the specified initial size and load factor.      *      * @param initialSize the initial size for the hash table      * @param loadFactor  the load factor for the hash table.      * @see HashMap#HashMap(int,float)      */     public SequencedHashMap(int initialSize, float loadFactor) {         sentinel = createSentinel();         entries = new HashMap(initialSize, loadFactor);     }     /**      * Construct a new sequenced hash map and add all the elements in the specified map. The order in which the mappings      * in the specified map are added is defined by {@link #putAll(Map)}.      */     public SequencedHashMap(Map m) {         this();         putAll(m);     }     /**      * Construct an empty sentinel used to hold the head (sentinel.next) and the tail (sentinel.prev) of the list. The      * sentinal has a <code>null</code> key and value.      */     private static final Entry createSentinel() {         Entry s = new Entry(null, null);         s.prev = s;         s.next = s;         return s;     }     /**      * Removes an internal entry from the linked list. This does not remove it from the underlying map.      */     private void removeEntry(Entry entry) {         entry.next.prev = entry.prev;         entry.prev.next = entry.next;     }     /**      * Inserts a new internal entry to the tail of the linked list. This does not add the entry to the underlying map.      */     private void insertEntry(Entry entry) {         entry.next = sentinel;         entry.prev = sentinel.prev;         sentinel.prev.next = entry;         sentinel.prev = entry;     }     // per Map.size()     /**      * Implements {@link Map#size()}.      */     public int size() {         // use the underlying Map's size since size is not maintained here.         return entries.size();     }     /**      * Implements {@link Map#isEmpty()}.      */     public boolean isEmpty() {         // for quick check whether the map is entry, we can check the linked list         // and see if there's anything in it.         return sentinel.next == sentinel;     }     /**      * Implements {@link Map#containsKey(Object)}.      */     public boolean containsKey(Object key) {         // pass on to underlying map implementation         return entries.containsKey(key);     }     /**      * Implements {@link Map#containsValue(Object)}.      */     public boolean containsValue(Object value) {         // unfortunately, we cannot just pass this call to the underlying map         // because we are mapping keys to entries, not keys to values. The         // underlying map doesn't have an efficient implementation anyway, so this         // isn't a big deal.         // do null comparison outside loop so we only need to do it once. This         // provides a tighter, more efficient loop at the expense of slight         // code duplication.         if (value == null) {             for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {                 if (pos.getValue() == null) {                     return true;                 }             }         } else {             for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {                 if (value.equals(pos.getValue())) {                     return true;                 }             }         }         return false;     }     /**      * Implements {@link Map#get(Object)}.      */     public Object get(Object o) {         // find entry for the specified key object         Entry entry = (Entry) entries.get(o);         if (entry == null) {             return null;         }         return entry.getValue();     }     /**      * Return the entry for the "oldest" mapping. That is, return the Map.Entry for the key-value pair that was first      * put into the map when compared to all the other pairings in the map. This behavior is equivalent to using      * <code>entrySet().iterator().next()</code>, but this method provides an optimized implementation.      *      * @return The first entry in the sequence, or <code>null</code> if the map is empty.      */     public Map.Entry getFirst() {         // sentinel.next points to the "first" element of the sequence -- the head         // of the list, which is exactly the entry we need to return. We must test         // for an empty list though because we don't want to return the sentinel!         return (isEmpty()) ? null : sentinel.next;     }     /**      * Return the key for the "oldest" mapping. That is, return the key for the mapping that was first put into the map      * when compared to all the other objects in the map. This behavior is equivalent to using      * <code>getFirst().getKey()</code>, but this method provides a slightly optimized implementation.      *      * @return The first key in the sequence, or <code>null</code> if the map is empty.      */     public Object getFirstKey() {         // sentinel.next points to the "first" element of the sequence -- the head         // of the list -- and the requisite key is returned from it. An empty list         // does not need to be tested. In cases where the list is empty,         // sentinel.next will point to the sentinel itself which has a null key,         // which is exactly what we would want to return if the list is empty (a         // nice convient way to avoid test for an empty list)         return sentinel.next.getKey();     }     /**      * Return the value for the "oldest" mapping. That is, return the value for the mapping that was first put into the      * map when compared to all the other objects in the map. This behavior is equivalent to using      * <code>getFirst().getValue()</code>, but this method provides a slightly optimized implementation.      *      * @return The first value in the sequence, or <code>null</code> if the map is empty.      */     public Object getFirstValue() {         // sentinel.next points to the "first" element of the sequence -- the head         // of the list -- and the requisite value is returned from it. An empty         // list does not need to be tested. In cases where the list is empty,         // sentinel.next will point to the sentinel itself which has a null value,         // which is exactly what we would want to return if the list is empty (a         // nice convient way to avoid test for an empty list)         return sentinel.next.getValue();     }     /**      * Return the entry for the "newest" mapping. That is, return the Map.Entry for the key-value pair that was first      * put into the map when compared to all the other pairings in the map. The behavior is equivalent to: <p/>      * <p/>      * <pre>      * Object obj = null;      * Iterator iter = entrySet().iterator();      * while (iter.hasNext()) {      *     obj = iter.next();      * }      * return (Map.Entry) obj;      * </pre>      * <p/>      * <p/>However, the implementation of this method ensures an O(1) lookup of the last key rather than O(n).      *      * @return The last entry in the sequence, or <code>null</code> if the map is empty.      */     public Map.Entry getLast() {         // sentinel.prev points to the "last" element of the sequence -- the tail         // of the list, which is exactly the entry we need to return. We must test         // for an empty list though because we don't want to return the sentinel!         return (isEmpty()) ? null : sentinel.prev;     }     /**      * Return the key for the "newest" mapping. That is, return the key for the mapping that was last put into the map      * when compared to all the other objects in the map. This behavior is equivalent to using      * <code>getLast().getKey()</code>, but this method provides a slightly optimized implementation.      *      * @return The last key in the sequence, or <code>null</code> if the map is empty.      */     public Object getLastKey() {         // sentinel.prev points to the "last" element of the sequence -- the tail         // of the list -- and the requisite key is returned from it. An empty list         // does not need to be tested. In cases where the list is empty,         // sentinel.prev will point to the sentinel itself which has a null key,         // which is exactly what we would want to return if the list is empty (a         // nice convient way to avoid test for an empty list)         return sentinel.prev.getKey();     }     /**      * Return the value for the "newest" mapping. That is, return the value for the mapping that was last put into the      * map when compared to all the other objects in the map. This behavior is equivalent to using      * <code>getLast().getValue()</code>, but this method provides a slightly optimized implementation.      *      * @return The last value in the sequence, or <code>null</code> if the map is empty.      */     public Object getLastValue() {         // sentinel.prev points to the "last" element of the sequence -- the tail         // of the list -- and the requisite value is returned from it. An empty         // list does not need to be tested. In cases where the list is empty,         // sentinel.prev will point to the sentinel itself which has a null value,         // which is exactly what we would want to return if the list is empty (a         // nice convient way to avoid test for an empty list)         return sentinel.prev.getValue();     }     /**      * Implements {@link Map#put(Object, Object)}.      */     public Object put(Object key, Object value) {         modCount++;         Object oldValue = null;         // lookup the entry for the specified key         Entry e = (Entry) entries.get(key);         // check to see if it already exists         if (e != null) {             // remove from list so the entry gets "moved" to the end of list             removeEntry(e);             // update value in map             oldValue = e.setValue(value);             // Note: We do not update the key here because its unnecessary. We only             // do comparisons using equals(Object) and we know the specified key and             // that in the map are equal in that sense. This may cause a problem if             // someone does not implement their hashCode() and/or equals(Object)             // method properly and then use it as a key in this map.         } else {             // add new entry             e = new Entry(key, value);             entries.put(key, e);         }         // assert(entry in map, but not list)         // add to list         insertEntry(e);         return oldValue;     }     /**      * Implements {@link Map#remove(Object)}.      */     public Object remove(Object key) {         Entry e = removeImpl(key);         return (e == null) ? null : e.getValue();     }     /**      * Fully remove an entry from the map, returning the old entry or null if there was no such entry with the specified      * key.      */     private Entry removeImpl(Object key) {         Entry e = (Entry) entries.remove(key);         if (e == null) {             return null;         }         modCount++;         removeEntry(e);         return e;     }     /**      * Adds all the mappings in the specified map to this map, replacing any mappings that already exist (as per      * {@linkMap#putAll(Map)}). The order in which the entries are added is determined by the iterator returned from      * {@linkMap#entrySet()}for the specified map.      *      * @param t the mappings that should be added to this map.      * @throws NullPointerException if <code>t</code> is <code>null</code>      */     public void putAll(Map t) {         Iterator iter = t.entrySet().iterator();         while (iter.hasNext()) {             Map.Entry entry = (Map.Entry) iter.next();             put(entry.getKey(), entry.getValue());         }     }     /**      * Implements {@link Map#clear()}.      */     public void clear() {         modCount++;         // remove all from the underlying map         entries.clear();         // and the list         sentinel.next = sentinel;         sentinel.prev = sentinel;     }     /**      * Implements {@link Map#equals(Object)}.      */     public boolean equals(Object obj) {         if (obj == null) {             return false;         }         if (obj == this) {             return true;         }         if (!(obj instanceof Map)) {             return false;         }         return entrySet().equals(((Map) obj).entrySet());     }     /**      * Implements {@link Map#hashCode()}.      */     public int hashCode() {         return entrySet().hashCode();     }     /**      * Provides a string representation of the entries within the map. The format of the returned string may change with      * different releases, so this method is suitable for debugging purposes only. If a specific format is required, use      * {@link #entrySet()}.{@link Set#iterator() iterator()}and iterate over the entries in the map formatting them      * as appropriate.      */     public String toString() {         StringBuffer buf = new StringBuffer();         buf.append('[');         for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {             buf.append(pos.getKey());             buf.append('=');             buf.append(pos.getValue());             if (pos.next != sentinel) {                 buf.append(',');             }         }         buf.append(']');         return buf.toString();     }     /**      * Implements {@link Map#keySet()}.      */     public Set keySet() {         return new AbstractSet() {             // required impls             public Iterator iterator() {                 return new OrderedIterator(KEY);             }             public boolean remove(Object o) {                 Entry e = SequencedHashMap.this.removeImpl(o);                 return (e != null);             }             // more efficient impls than abstract set             public void clear() {                 SequencedHashMap.this.clear();             }             public int size() {                 return SequencedHashMap.this.size();             }             public boolean isEmpty() {                 return SequencedHashMap.this.isEmpty();             }             public boolean contains(Object o) {                 return SequencedHashMap.this.containsKey(o);             }         };     }     /**      * Implements {@link Map#values()}.      */     public Collection values() {         return new AbstractCollection() {             // required impl             public Iterator iterator() {                 return new OrderedIterator(VALUE);             }             public boolean remove(Object value) {                 // do null comparison outside loop so we only need to do it once. This                 // provides a tighter, more efficient loop at the expense of slight                 // code duplication.                 if (value == null) {                     for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {                         if (pos.getValue() == null) {                             SequencedHashMap.this.removeImpl(pos.getKey());                             return true;                         }                     }                 } else {                     for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {                         if (value.equals(pos.getValue())) {                             SequencedHashMap.this.removeImpl(pos.getKey());                             return true;                         }                     }                 }                 return false;             }             // more efficient impls than abstract collection             public void clear() {                 SequencedHashMap.this.clear();             }             public int size() {                 return SequencedHashMap.this.size();             }             public boolean isEmpty() {                 return SequencedHashMap.this.isEmpty();             }             public boolean contains(Object o) {                 return SequencedHashMap.this.containsValue(o);             }         };     }     /**      * Implements {@link Map#entrySet()}.      */     public Set entrySet() {         return new AbstractSet() {             // helper             private Entry findEntry(Object o) {                 if (o == null) {                     return null;                 }                 if (!(o instanceof Map.Entry)) {                     return null;                 }                 Map.Entry e = (Map.Entry) o;                 Entry entry = (Entry) entries.get(e.getKey());                 if ((entry != null) && entry.equals(e)) {                     return entry;                 } else {                     return null;                 }             }             // required impl             public Iterator iterator() {                 return new OrderedIterator(ENTRY);             }             public boolean remove(Object o) {                 Entry e = findEntry(o);                 if (e == null) {                     return false;                 }                 return SequencedHashMap.this.removeImpl(e.getKey()) != null;             }             // more efficient impls than abstract collection             public void clear() {                 SequencedHashMap.this.clear();             }             public int size() {                 return SequencedHashMap.this.size();             }             public boolean isEmpty() {                 return SequencedHashMap.this.isEmpty();             }             public boolean contains(Object o) {                 return findEntry(o) != null;             }         };     }     // APIs maintained from previous version of SequencedHashMap for backwards     // compatibility     /**      * Creates a shallow copy of this object, preserving the internal structure by copying only references. The keys and      * values themselves are not <code>clone()</code> 'd. The cloned object maintains the same sequence.      *      * @return A clone of this instance.      * @throws CloneNotSupportedException if clone is not supported by a subclass.      */     public Object clone() throws CloneNotSupportedException {         // yes, calling super.clone() silly since we're just blowing away all         // the stuff that super might be doing anyway, but for motivations on         // this, see:         // http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html         SequencedHashMap map = (SequencedHashMap) super.clone();         // create new, empty sentinel         map.sentinel = createSentinel();         // create a new, empty entry map         // note: this does not preserve the initial capacity and load factor.         map.entries = new HashMap();         // add all the mappings         map.putAll(this);         // Note: We cannot just clone the hashmap and sentinel because we must         // duplicate our internal structures. Cloning those two will not clone all         // the other entries they reference, and so the cloned hash map will not be         // able to maintain internal consistency because there are two objects with         // the same entries. See discussion in the Entry implementation on why we         // cannot implement a clone of the Entry (and thus why we need to recreate         // everything).         return map;     }     /**      * Returns the Map.Entry at the specified index      *      * @throws ArrayIndexOutOfBoundsException if the specified index is <code>&lt; 0</code> or <code>&gt;</code> the      *                                        size of the map.      */     private Map.Entry getEntry(int index) {         Entry pos = sentinel;         if (index < 0) {             throw new ArrayIndexOutOfBoundsException(index + " < 0");         }         // loop to one before the position         int i = -1;         while ((i < (index - 1)) && (pos.next != sentinel)) {             i++;             pos = pos.next;         }         // pos.next is the requested position         // if sentinel is next, past end of list         if (pos.next == sentinel) {             throw new ArrayIndexOutOfBoundsException(index + " >= " + (i + 1));         }         return pos.next;     }     /**      * Returns the key at the specified index.      *      * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is <code>&lt; 0</code> or <code>&gt;</code>      *                                        the size of the map.      */     public Object get(int index) {         return getEntry(index).getKey();     }     /**      * Returns the value at the specified index.      *      * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is <code>&lt; 0</code> or <code>&gt;</code>      *                                        the size of the map.      */     public Object getValue(int index) {         return getEntry(index).getValue();     }     /**      * Returns the index of the specified key.      */     public int indexOf(Object key) {         Entry e = (Entry) entries.get(key);         int pos = 0;         while (e.prev != sentinel) {             pos++;             e = e.prev;         }         return pos;     }     /**      * Returns a key iterator.      */     public Iterator iterator() {         return keySet().iterator();     }     /**      * Returns the last index of the specified key.      */     public int lastIndexOf(Object key) {         // keys in a map are guarunteed to be unique         return indexOf(key);     }     /**      * Returns a List view of the keys rather than a set view. The returned list is unmodifiable. This is required      * because changes to the values of the list (using {@link java.util.ListIterator#set(Object)}) will effectively      * remove the value from the list and reinsert that value at the end of the list, which is an unexpected side effect      * of changing the value of a list. This occurs because changing the key, changes when the mapping is added to the      * map and thus where it appears in the list. <p/>      * <P>      * An alternative to this method is to use {@link #keySet()}      *      * @return The ordered list of keys.      * @see #keySet()      */     public List sequence() {         List l = new ArrayList(size());         Iterator iter = keySet().iterator();         while (iter.hasNext()) {             l.add(iter.next());         }         return Collections.unmodifiableList(l);     }     /**      * Removes the element at the specified index.      *      * @param index The index of the object to remove.      * @return The previous value coressponding the <code>key</code>, or <code>null</code> if none existed.      * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is <code>&lt; 0</code> or <code>&gt;</code>      *                                        the size of the map.      */     public Object remove(int index) {         return remove(get(index));     }     // per Externalizable.readExternal(ObjectInput)     /**      * Deserializes this map from the given stream.      *      * @param in the stream to deserialize from      * @throws IOException            if the stream raises it      * @throws ClassNotFoundException if the stream raises it      */     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {         int size = in.readInt();         for (int i = 0; i < size; i++) {             Object key = in.readObject();             Object value = in.readObject();             put(key, value);         }     }     /**      * Serializes this map to the given stream.      *      * @param out the stream to serialize to      * @throws IOException if the stream raises it      */     public void writeExternal(ObjectOutput out) throws IOException {         out.writeInt(size());         for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {             out.writeObject(pos.getKey());             out.writeObject(pos.getValue());         }     }     /**      * {@link java.util.Map.Entry}that doubles as a node in the linked list of sequenced mappings.      */     private static class Entry implements Map.Entry {         // Note: This class cannot easily be made clonable. While the actual         // implementation of a clone would be simple, defining the semantics is         // difficult. If a shallow clone is implemented, then entry.next.prev !=         // entry, which is unintuitive and probably breaks all sorts of assumptions         // in code that uses this implementation. If a deep clone is         // implementated, then what happens when the linked list is cyclical (as is         // the case with SequencedHashMap)? It's impossible to know in the clone         // when to stop cloning, and thus you end up in a recursive loop,         // continuously cloning the "next" in the list.         private final Object key;         private Object value;         // package private to allow the SequencedHashMap to access and manipulate         // them.         Entry next = null;         Entry prev = null;         public Entry(Object key, Object value) {             this.key = key;             this.value = value;         }         // per Map.Entry.getKey()         public Object getKey() {             return this.key;         }         // per Map.Entry.getValue()         public Object getValue() {             return this.value;         }         // per Map.Entry.setValue()         public Object setValue(Object value) {             Object oldValue = this.value;             this.value = value;             return oldValue;         }         public int hashCode() {             // implemented per api docs for Map.Entry.hashCode()             return (((getKey() == null) ? 0 : getKey().hashCode()) ^                     ((getValue() == null) ? 0 : getValue().hashCode()));         }         public boolean equals(Object obj) {             if (obj == null) {                 return false;             }             if (obj == this) {                 return true;             }             if (!(obj instanceof Map.Entry)) {                 return false;             }             Map.Entry other = (Map.Entry) obj;             // implemented per api docs for Map.Entry.equals(Object)             return (((getKey() == null) ? (other.getKey() == null) : getKey().equals(other.getKey())) && ((getValue() ==                                                                                                            null)                                                                                                           ?                                                                                                           (other.getValue() ==                                                                                                            null)                                                                                                           :                                                                                                           getValue()                     .equals(other.getValue())));         }         public String toString() {             return "[" + getKey() + '=' + getValue() + ']';         }     }     private class OrderedIterator implements Iterator {         /**          * Holds the type that should be returned from the iterator. The value should be either {@link #KEY},          * {@link#VALUE}, or {@link #ENTRY}. To save a tiny bit of memory, this field is also used as a marker for          * when remove has been called on the current object to prevent a second remove on the same element.          * Essientially, if this value is negative (i.e. the bit specified by {@link #REMOVED_MASK}is set), the current          * position has been removed. If positive, remove can still be called.          */         private int returnType;         /**          * Holds the "current" position in the iterator. When pos.next is the sentinel, we've reached the end of the          * list.          */         private Entry pos = sentinel;         /**          * Holds the expected modification count. If the actual modification count of the map differs from this value,          * then a concurrent modification has occurred.          */         private transient long expectedModCount = modCount;         /**          * Construct an iterator over the sequenced elements in the order in which they were added. The {@link #next()}          * method returns the type specified by <code>returnType</code> which must be either {@link #KEY},          * {@link#VALUE}, or {@link #ENTRY}.          */         public OrderedIterator(int returnType) {             //// Since this is a private inner class, nothing else should have             //// access to the constructor. Since we know the rest of the outer             //// class uses the iterator correctly, we can leave of the following             //// check:             //if(returnType >= 0 && returnType <= 2) {             //  throw new IllegalArgumentException("Invalid iterator type");             //}             // Set the "removed" bit so that the iterator starts in a state where             // "next" must be called before "remove" will succeed.             this.returnType = returnType | REMOVED_MASK;         }         /**          * Returns whether there is any additional elements in the iterator to be returned.          *          * @return <code>true</code> if there are more elements left to be returned from the iterator;          *         <code>false</code> otherwise.          */         public boolean hasNext() {             return pos.next != sentinel;         }         /**          * Returns the next element from the iterator.          *          * @return the next element from the iterator.          * @throws NoSuchElementException if there are no more elements in the iterator.          * @throws ConcurrentModificationException          *                                if a modification occurs in the underlying map.          */         public Object next() {             if (modCount != expectedModCount) {                 throw new ConcurrentModificationException();             }             if (pos.next == sentinel) {                 throw new NoSuchElementException();             }             // clear the "removed" flag             returnType = returnType & ~REMOVED_MASK;             pos = pos.next;             switch (returnType) {                 case KEY:                     return pos.getKey();                 case VALUE:                     return pos.getValue();                 case ENTRY:                     return pos;                 default:                     // should never happen                     throw new Error("bad iterator type: " + returnType);             }         }         /**          * Removes the last element returned from the {@link #next()}method from the sequenced map.          *          * @throws IllegalStateException if there isn't a "last element" to be removed. That is, if {@link #next()}has          *                               never been called, or if {@link #remove()}was already called on the element.          * @throws ConcurrentModificationException          *                               if a modification occurs in the underlying map.          */         public void remove() {             if ((returnType & REMOVED_MASK) != 0) {                 throw new IllegalStateException("remove() must follow next()");             }             if (modCount != expectedModCount) {                 throw new ConcurrentModificationException();             }             SequencedHashMap.this.removeImpl(pos.getKey());             // update the expected mod count for the remove operation             expectedModCount++;             // set the removed flag             returnType = returnType | REMOVED_MASK;         }     } }