FastMap.java Source code

Java tutorial

Introduction

Here is the source code for FastMap.java

Source

/*
 * J.A.D.E. Java(TM) Addition to Default Environment.
 * Latest release available at http://jade.dautelle.com/
 * This class is public domain (not copyrighted).
 *
 * This class was added from J.A.D.E. directly to avoid adding the jade.jar
 * which was causing a conflict with parsing XML menus in FreeHep for some reason
 */

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * <p> This class represents a <code>Map</code> collection with real-time 
 *     behavior. Unless the map's size exceeds its current capacity, 
 *     no dynamic memory allocation is ever performed and response time is 
 *     <b>extremely fast</b> and <b>consistent</b>.</p>
 *  
 * <p> Our <a href="http://jade.dautelle.com/doc/benchmark.txt">benchmark</a> 
 *     indicates that {@link FastMap#put FastMap.put(key, value)} is up to 
 *     <b>5x faster</b> than <code>java.util.HashMap.put(key, value)</code>.
 *     This difference is mostly due to the cost of the <code>Map.Entry</code>
 *     allocations that {@link FastMap} avoids by recycling its entries 
 *     (see note below).</p>
 * 
 * <p> {@link FastMap} has a predictable iteration order, which is the order
 *     in which keys were inserted into the map (similar to 
 *     <code>java.util.LinkedHashMap</code> collection class).
 *     A bi-directional list iterator over the map entries is also 
 *     {@link #fastIterator provided}, this iterator can be moved 
 *     to the {@link FastIterator#toFirst first} or to the 
 *     {@link FastIterator#toLast last} entry for unlimited reuse.</p>
 * 
 * <p> Applications may change the resizing policy  of {@link FastMap} 
 *     by overriding the {@link #sizeChanged} method. For example, to reduce 
 *     memory footprint, the map's capacity could be maitained at 50% of 
 *     the current map's size.</p>  
 *
 * <p> This implementation is not synchronized. Multiple threads accessing
 *     or modifying the collection must be synchronized externally.</p>
 * 
 * <p> <b>Note:</b> To avoid dynamic memory allocations, {@link FastMap}
 *     maintains an internal pool of <code>Map.Entry</code> objects. The size
 *     of the pool is determined by the map's capacity. When an entry is
 *     removed from the map, it is automatically restored to the pool.</p>
 * 
 * <p><i> This class is <b>public domain</b> (not copyrighted).</i></p>
 *  
 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 6.0, January 18 2004
 */
public class FastMap implements Map, Cloneable, Serializable {

    /**
     * Holds the map's hash table.
     */
    private transient EntryImpl[] _entries;

    /**
     * Holds the map's current capacity.
     */
    private transient int _capacity;

    /**
     * Holds the hash code mask.
     */
    private transient int _mask;

    /**
     * Holds the first pool entry (linked list).
     */
    private transient EntryImpl _poolFirst;

    /**
     * Holds the first map entry (linked list).
     */
    private transient EntryImpl _mapFirst;

    /**
     * Holds the last map entry (linked list).
     */
    private transient EntryImpl _mapLast;

    /**
     * Holds the current size.
     */
    private transient int _size;

    /**
     * Creates a {@link FastMap} with a capacity of <code>16</code> entries.
     */
    public FastMap() {
        initialize(16);
    }

    /**
     * Creates a {@link FastMap}, copy of the specified <code>Map</code>.
     * If the specified map is not an instance of {@link FastMap}, the 
     * newly created map has a capacity set to the specified map's size.
     * The copy has the same order as the original, regardless of the original 
     * map's implementation:<pre>
     *     TreeMap dictionary = ...;
     *     FastMap dictionaryLookup = new FastMap(dictionary);
     * </pre>
     * 
     * @param  map the map whose mappings are to be placed in this map.
     */
    public FastMap(Map map) {
        int capacity = (map instanceof FastMap) ? ((FastMap) map).capacity() : map.size();
        initialize(capacity);
        putAll(map);
    }

    /**
     * Creates a {@link FastMap} with the specified capacity. Unless the 
     * capacity is exceeded, operations on this map do not allocate entries.
     * For optimum performance, the capacity should be of the same order 
     * of magnitude or larger than the expected map's size.
     * 
     * @param  capacity the number of buckets in the hash table; it also 
     *         defines the number of pre-allocated entries.
     */
    public FastMap(int capacity) {
        initialize(capacity);
    }

    /**
     * Returns the number of key-value mappings in this {@link FastMap}. 
     *
     * @return this map's size.
     */
    public int size() {
        return _size;
    }

    /**
     * Returns the capacity of this {@link FastMap}. The capacity defines
     * the number of buckets in the hash table, as well as the maximum number
     * of entries the map may contain without allocating memory.
     *
     * @return this map's capacity.
     */
    public int capacity() {
        return _capacity;
    }

    /**
     * Indicates if this {@link FastMap} contains no key-value mappings.
     *
     * @return <code>true</code> if this map contains no key-value mappings;
     *         <code>false</code> otherwise.
     */
    public boolean isEmpty() {
        return _size == 0;
    }

    /**
     * Indicates if this {@link FastMap} contains a mapping for the specified
     * key.
     *
     * @param   key the key whose presence in this map is to be tested.
     * @return <code>true</code> if this map contains a mapping for the
     *         specified key; <code>false</code> otherwise.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public boolean containsKey(Object key) {
        EntryImpl entry = _entries[keyHash(key) & _mask];
        while (entry != null) {
            if (key.equals(entry._key)) {
                return true;
            }
            entry = entry._next;
        }
        return false;
    }

    /**
     * Indicates if this {@link FastMap} maps one or more keys to the
     * specified value.
     *
     * @param  value the value whose presence in this map is to be tested.
     * @return <code>true</code> if this map maps one or more keys to the
     *         specified value.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public boolean containsValue(Object value) {
        EntryImpl entry = _mapFirst;
        while (entry != null) {
            if (value.equals(entry._value)) {
                return true;
            }
            entry = entry._after;
        }
        return false;
    }

    /**
     * Returns the value to which this {@link FastMap} maps the specified key. 
     *
     * @param  key the key whose associated value is to be returned.
     * @return the value to which this map maps the specified key,
     *         or <code>null</code> if there is no mapping for the key.
     * @throws NullPointerException if key is <code>null</code>.
     */
    public Object get(Object key) {
        EntryImpl entry = _entries[keyHash(key) & _mask];
        while (entry != null) {
            if (key.equals(entry._key)) {
                return entry._value;
            }
            entry = entry._next;
        }
        return null;
    }

    /**
     * Returns the entry with the specified key. 
     * 
     * @param key the key whose associated entry is to be returned.
     * @return the entry for the specified key or <code>null</code> if none.
     */
    public Entry getEntry(Object key) {
        EntryImpl entry = _entries[keyHash(key) & _mask];
        while (entry != null) {
            if (key.equals(entry._key)) {
                return entry;
            }
            entry = entry._next;
        }
        return null;
    }

    /**
     * Associates the specified value with the specified key in this 
     * {@link FastMap}. If the {@link FastMap} previously contained a mapping
     * for this key, the old value is replaced.
     *
     * @param  key the key with which the specified value is to be associated.
     * @param  value the value to be associated with the specified key.
     * @return the previous value associated with specified key,
     *         or <code>null</code> if there was no mapping for key.
     *         A <code>null</code> return can also indicate that the map 
     *         previously associated <code>null</code> with the specified key.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public Object put(Object key, Object value) {
        EntryImpl entry = _entries[keyHash(key) & _mask];
        while (entry != null) {
            if (key.equals(entry._key)) {
                Object prevValue = entry._value;
                entry._value = value;
                return prevValue;
            }
            entry = entry._next;
        }
        // No previous mapping.
        addEntry(key, value);
        return null;
    }

    /**
     * Returns a reusable {@link FastIterator} over this {@link FastMap} entries
     * (unique instance per map). For example:<pre> 
     *     // Iteration without memory allocation!
     *     for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
     *         Entry entry = i.nextEntry();
     *         ...
     *     }</pre>    
     * 
     * @return an iterator which can be reset for reuse over and over.
     * @see    FastMap.FastIterator
     */
    public FastIterator fastIterator() {
        return _fastIterator;
    }

    private final FastIterator _fastIterator = new FastIterator();

    /**
     * Copies all of the mappings from the specified map to this 
     * {@link FastMap}.
     *
     * @param  map the mappings to be stored in this map.
     * @throws NullPointerException the specified map is <code>null</code>, or
     *         the specified map contains <code>null</code> keys.
     */
    public void putAll(Map map) {
        for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
            Entry e = (Entry) i.next();
            addEntry(e.getKey(), e.getValue());
        }
    }

    /**
     * Removes the mapping for this key from this {@link FastMap} if present.
     *
     * @param  key the key whose mapping is to be removed from the map.
     * @return previous value associated with specified key,
     *         or <code>null</code> if there was no mapping for key.
     *         A <code>null</code> return can also indicate that the map 
     *         previously associated <code>null</code> with the specified key.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public Object remove(Object key) {
        EntryImpl entry = _entries[keyHash(key) & _mask];
        while (entry != null) {
            if (key.equals(entry._key)) {
                Object prevValue = entry._value;
                removeEntry(entry);
                return prevValue;
            }
            entry = entry._next;
        }
        return null;
    }

    /**
     * Removes all mappings from this {@link FastMap}.
     */
    public void clear() {
        // Clears all keys, values and buckets linked lists.
        for (EntryImpl entry = _mapFirst; entry != null; entry = entry._after) {
            entry._key = null;
            entry._value = null;
            entry._before = null;
            entry._next = null;
            if (entry._previous == null) { // First in bucket.
                _entries[entry._index] = null;
            } else {
                entry._previous = null;
            }
        }

        // Recycles all entries.
        if (_mapLast != null) {
            _mapLast._after = _poolFirst; // Connects to pool.
            _poolFirst = _mapFirst;
            _mapFirst = null;
            _mapLast = null;
            _size = 0;
            sizeChanged();
        }
    }

    /**
     * Changes the current capacity of this {@link FastMap}. If the capacity
     * is increased, new entries are allocated and added to the pool. 
     * If the capacity is decreased, entries from the pool are deallocated 
     * (and are garbage collected eventually). The capacity also determined 
     * the number of buckets for the hash table.
     * 
     * @param newCapacity the new capacity of this map.
     */
    public void setCapacity(int newCapacity) {
        if (newCapacity > _capacity) { // Capacity increases.
            for (int i = _capacity; i < newCapacity; i++) {
                EntryImpl entry = new EntryImpl();
                entry._after = _poolFirst;
                _poolFirst = entry;
            }
        } else if (newCapacity < _capacity) { // Capacity decreases.
            for (int i = newCapacity; (i < _capacity) && (_poolFirst != null); i++) {
                // Disconnects the entry for gc to do its work.
                EntryImpl entry = _poolFirst;
                _poolFirst = entry._after;
                entry._after = null; // All pointers are now null!
            }
        }
        // Find a power of 2 >= capacity
        int tableLength = 16;
        while (tableLength < newCapacity) {
            tableLength <<= 1;
        }
        // Checks if the hash table has to be re-sized.
        if (_entries.length != tableLength) {
            _entries = new EntryImpl[tableLength];
            _mask = tableLength - 1;

            // Repopulates the hash table.
            EntryImpl entry = _mapFirst;
            while (entry != null) {
                int index = keyHash(entry._key) & _mask;
                entry._index = index;

                // Connects to bucket.
                entry._previous = null; // Resets previous.
                EntryImpl next = _entries[index];
                entry._next = next;
                if (next != null) {
                    next._previous = entry;
                }
                _entries[index] = entry;

                entry = entry._after;
            }
        }
        _capacity = newCapacity;
    }

    /**
     * Returns a shallow copy of this {@link FastMap}. The keys and
     * the values themselves are not cloned.
     *
     * @return a shallow copy of this map.
     */
    public Object clone() {
        try {
            FastMap clone = (FastMap) super.clone();
            clone.initialize(_capacity);
            clone.putAll(this);
            return clone;
        } catch (CloneNotSupportedException e) {
            // Should not happen, since we are Cloneable.
            throw new InternalError();
        }
    }

    /**
     * Compares the specified object with this {@link FastMap} for equality. 
     * Returns <code>true</code> if the given object is also a map and the two
     * maps represent the same mappings (regardless of collection iteration
     * order).
     *
     * @param obj the object to be compared for equality with this map.
     * @return <code>true</code> if the specified object is equal to this map;
     *         <code>false</code> otherwise.
     */
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (obj instanceof Map) {
            Map that = (Map) obj;
            if (this.size() == that.size()) {
                EntryImpl entry = _mapFirst;
                while (entry != null) {
                    if (!that.entrySet().contains(entry)) {
                        return false;
                    }
                    entry = entry._after;
                }
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * Returns the hash code value for this {@link FastMap}.
     *
     * @return the hash code value for this map.
     */
    public int hashCode() {
        int code = 0;
        EntryImpl entry = _mapFirst;
        while (entry != null) {
            code += entry.hashCode();
            entry = entry._after;
        }
        return code;
    }

    /**
     * Returns a <code>String</code> representation of this {@link FastMap}.
     *
     * @return <code>this.entrySet().toString();</code>
     */
    public String toString() {
        return entrySet().toString();
    }

    /**
     * Returns a collection view of the values contained in this 
     * {@link FastMap}.  The collection is backed by the map, so changes to
     * the map are reflected in the collection, and vice-versa. 
     * The collection supports element removal, which removes the corresponding
     * mapping from this map, via the
     * <code>Iterator.remove</code>, <code>Collection.remove</code>,
     * <code>removeAll</code>, <code>retainAll</code>, 
     * and <code>clear</code> operations. It does not support the 
     * <code>add</code> or <code>addAll</code> operations.
     *
     * @return a collection view of the values contained in this map.
     */
    public Collection values() {
        return _values;
    }

    private transient Values _values;

    private class Values extends AbstractCollection {
        public Iterator iterator() {
            return new Iterator() {
                EntryImpl after = _mapFirst;
                EntryImpl before;

                public void remove() {
                    if (before != null) {
                        removeEntry(before);
                    } else {
                        throw new IllegalStateException();
                    }
                }

                public boolean hasNext() {
                    return after != null;
                }

                public Object next() {
                    if (after != null) {
                        before = after;
                        after = after._after;
                        return before._value;
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }

        public int size() {
            return _size;
        }

        public boolean contains(Object o) {
            return containsValue(o);
        }

        public void clear() {
            FastMap.this.clear();
        }
    }

    /**
     * Returns a collection view of the mappings contained in this
     * {@link FastMap}. Each element in the returned collection is a
     * <code>Map.Entry</code>.  The collection is backed by the map,
     * so changes to the map are reflected in the collection, and vice-versa. 
     * The collection supports element removal, which removes the corresponding
     * mapping from this map, via the
     * <code>Iterator.remove</code>, <code>Collection.remove</code>,
     * <code>removeAll</code>, <code>retainAll</code>, 
     * and <code>clear</code> operations. It does not support the 
     * <code>add</code> or <code>addAll</code> operations.
     *
     * @return a collection view of the mappings contained in this map.
     */
    public Set entrySet() {
        return _entrySet;
    }

    private transient EntrySet _entrySet;

    private class EntrySet extends AbstractSet {
        public Iterator iterator() {
            return new Iterator() {
                EntryImpl after = _mapFirst;
                EntryImpl before;

                public void remove() {
                    if (before != null) {
                        removeEntry(before);
                    } else {
                        throw new IllegalStateException();
                    }
                }

                public boolean hasNext() {
                    return after != null;
                }

                public Object next() {
                    if (after != null) {
                        before = after;
                        after = after._after;
                        return before;
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }

        public int size() {
            return _size;
        }

        public boolean contains(Object obj) { // Optimization.
            if (obj instanceof Entry) {
                Entry entry = (Entry) obj;
                Entry mapEntry = getEntry(entry.getKey());
                return entry.equals(mapEntry);
            } else {
                return false;
            }
        }

        public boolean remove(Object obj) { // Optimization.
            if (obj instanceof Entry) {
                Entry entry = (Entry) obj;
                EntryImpl mapEntry = (EntryImpl) getEntry(entry.getKey());
                if ((mapEntry != null) && (entry.getValue()).equals(mapEntry._value)) {
                    removeEntry(mapEntry);
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * Returns a set view of the keys contained in this {@link FastMap}. 
     * The set is backed by the map, so changes to the map are reflected 
     * in the set, and vice-versa.  The set supports element removal,
     * which removes the corresponding mapping from this map, via the
     * <code>Iterator.remove</code>, <code>Collection.remove</code>,
     * <code>removeAll</code>, <code>retainAll</code>, 
     * and <code>clear</code> operations. It does not support the 
     * <code>add</code> or <code>addAll</code> operations.
     *
     * @return a set view of the keys contained in this map.
     */
    public Set keySet() {
        return _keySet;
    }

    private transient KeySet _keySet;

    private class KeySet extends AbstractSet {
        public Iterator iterator() {
            return new Iterator() {
                EntryImpl after = _mapFirst;
                EntryImpl before;

                public void remove() {
                    if (before != null) {
                        removeEntry(before);
                    } else {
                        throw new IllegalStateException();
                    }
                }

                public boolean hasNext() {
                    return after != null;
                }

                public Object next() {
                    if (after != null) {
                        before = after;
                        after = after._after;
                        return before._key;
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }

        public int size() {
            return _size;
        }

        public boolean contains(Object obj) { // Optimization.
            return FastMap.this.containsKey(obj);
        }

        public boolean remove(Object obj) { // Optimization.
            return FastMap.this.remove(obj) != null;
        }

        public void clear() { // Optimization.
            FastMap.this.clear();
        }
    }

    /**
     * This methods is being called when the size of this {@link FastMap} 
     * has changed. The default behavior is to double the map's capacity 
     * when the map's size exceeds the current map's capacity.
     * Sub-class may override this method to implement custom resizing 
     * policies or to disable automatic resizing. For example:<pre>
     *     Map fixedCapacityMap = new FastMap(256) { 
     *           protected sizeChanged() {
     *               // Do nothing, automatic resizing disabled.
     *           }
     *     };</pre>
     * @see #setCapacity
     */
    protected void sizeChanged() {
        if (size() > capacity()) {
            setCapacity(capacity() * 2);
        }
    }

    /**
     * Returns the hash code for the specified key. The formula being used 
     * is identical to the formula used by <code>java.util.HashMap</code>
     * (ensures similar behavior for ill-conditioned hashcode keys).
     * 
     * @param key the key to calculate the hashcode for.
     * @return the hash code for the specified key.
     */
    private static int keyHash(Object key) {
        // From HashMap.hash(Object) function.
        int hashCode = key.hashCode();
        hashCode += ~(hashCode << 9);
        hashCode ^= (hashCode >>> 14);
        hashCode += (hashCode << 4);
        hashCode ^= (hashCode >>> 10);
        return hashCode;
    }

    /**
     * Adds a new entry for the specified key and value.
     * @param key the entry's key.
     * @param value the entry's value.
     */
    private void addEntry(Object key, Object value) {
        EntryImpl entry = _poolFirst;
        if (entry != null) {
            _poolFirst = entry._after;
            entry._after = null;
        } else { // Pool empty.
            entry = new EntryImpl();
        }

        // Setup entry parameters.
        entry._key = key;
        entry._value = value;
        int index = keyHash(key) & _mask;
        entry._index = index;

        // Connects to bucket.
        EntryImpl next = _entries[index];
        entry._next = next;
        if (next != null) {
            next._previous = entry;
        }
        _entries[index] = entry;

        // Connects to collection.
        if (_mapLast != null) {
            entry._before = _mapLast;
            _mapLast._after = entry;
        } else {
            _mapFirst = entry;
        }
        _mapLast = entry;

        // Updates size.
        _size++;
        sizeChanged();
    }

    /**
     * Removes the specified entry from the map.
     * 
     * @param entry the entry to be removed.
     */
    private void removeEntry(EntryImpl entry) {

        // Removes from bucket.
        EntryImpl previous = entry._previous;
        EntryImpl next = entry._next;
        if (previous != null) {
            previous._next = next;
            entry._previous = null;
        } else { // First in bucket.
            _entries[entry._index] = next;
        }
        if (next != null) {
            next._previous = previous;
            entry._next = null;
        } // Else do nothing, no last pointer.

        // Removes from collection.
        EntryImpl before = entry._before;
        EntryImpl after = entry._after;
        if (before != null) {
            before._after = after;
            entry._before = null;
        } else { // First in collection.
            _mapFirst = after;
        }
        if (after != null) {
            after._before = before;
        } else { // Last in collection.
            _mapLast = before;
        }

        // Clears value and key.
        entry._key = null;
        entry._value = null;

        // Recycles.
        entry._after = _poolFirst;
        _poolFirst = entry;

        // Updates size.
        _size--;
        sizeChanged();
    }

    /**
     * Initializes this instance for the specified capacity.
     * Once initialized, operations on this map should not create new objects 
     * (unless the map's size exceeds the specified capacity). 
     *  
     * @param capacity the initial capacity.
     */
    private void initialize(int capacity) {
        // Find a power of 2 >= capacity
        int tableLength = 16;
        while (tableLength < capacity) {
            tableLength <<= 1;
        }
        // Allocates hash table.
        _entries = new EntryImpl[tableLength];
        _mask = tableLength - 1;
        _capacity = capacity;
        _size = 0;
        // Allocates views.
        _values = new Values();
        _entrySet = new EntrySet();
        _keySet = new KeySet();
        // Resets pointers.
        _poolFirst = null;
        _mapFirst = null;
        _mapLast = null;
        // Allocates entries.
        for (int i = 0; i < capacity; i++) {
            EntryImpl entry = new EntryImpl();
            entry._after = _poolFirst;
            _poolFirst = entry;
        }
    }

    /**
     * Requires special handling during de-serialization process.
     *
     * @param  stream the object input stream.
     * @throws IOException if an I/O error occurs.
     * @throws ClassNotFoundException if the class for the object de-serialized
     *         is not found.
     */
    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
        int capacity = stream.readInt();
        initialize(capacity);
        int size = stream.readInt();
        for (int i = 0; i < size; i++) {
            Object key = stream.readObject();
            Object value = stream.readObject();
            addEntry(key, value);
        }
    }

    /**
     * Requires special handling during serialization process.
     *
     * @param  stream the object output stream.
     * @throws IOException if an I/O error occurs.
     */
    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.writeInt(_capacity);
        stream.writeInt(_size);
        int count = 0;
        EntryImpl entry = _mapFirst;
        while (entry != null) {
            stream.writeObject(entry._key);
            stream.writeObject(entry._value);
            count++;
            entry = entry._after;
        }
        if (count != _size) {
            throw new IOException("FastMap Corrupted");
        }
    }

    /**
     * This inner class represents a reusable list iterator over
     * {@link FastMap} entries. This iterator is bi-directional and can be 
     * directly moved to the {@link #toFirst first} or {@link #toLast last}
     * entry. For example:<pre>
     *     for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
     *         Entry entry = i.nextEntry();
     *         ...
     *     }</pre>    
     * {@link #set setting} or {@link #add adding} new entries is not
     * supported.
     */
    public final class FastIterator implements ListIterator {
        EntryImpl after = _mapFirst;
        EntryImpl before;
        int nextIndex = 0;

        public FastIterator toFirst() {
            after = _mapFirst;
            before = null;
            nextIndex = 0;
            return this;
        }

        public FastIterator toLast() {
            after = null;
            before = _mapLast;
            nextIndex = _size;
            return this;
        }

        public boolean hasNext() {
            return after != null;
        }

        public Entry nextEntry() {
            if (after != null) {
                nextIndex++;
                before = after;
                after = after._after;
                return before;
            } else {
                throw new NoSuchElementException();
            }
        }

        public Object next() {
            return nextEntry();
        }

        public boolean hasPrevious() {
            return before != null;
        }

        public Entry previousEntry() {
            if (before != null) {
                nextIndex--;
                after = before;
                before = before._after;
                return after;
            } else {
                throw new NoSuchElementException();
            }
        }

        public Object previous() {
            return previousEntry();
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex - 1;
        }

        public void remove() {
            if (before != null) {
                removeEntry(before);
            } else {
                throw new IllegalStateException();
            }
        }

        public void set(Object o) {
            throw new UnsupportedOperationException();
        }

        public void add(Object o) {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * This class represents a {@link FastMap} entry.
     */
    private static final class EntryImpl implements Entry {

        /**
         * Holds the entry key (null when in pool).
         */
        private Object _key;

        /**
         * Holds the entry value (null when in pool).
         */
        private Object _value;

        /**
         * Holds the bucket index (undefined when in pool). 
         */
        private int _index;

        /**
         * Holds the previous entry in the same bucket (null when in pool).
         */
        private EntryImpl _previous;

        /**
         * Holds the next entry in the same bucket (null when in pool).
         */
        private EntryImpl _next;

        /**
         * Holds the entry added before this entry (null when in pool).
         */
        private EntryImpl _before;

        /**
         * Holds the entry added after this entry 
         * or the next available entry when in pool.
         */
        private EntryImpl _after;

        /**
         * Returns the key for this entry.
         * 
         * @return the entry's key.
         */
        public Object getKey() {
            return _key;
        }

        /**
         * Returns the value for this entry.
         * 
         * @return the entry's value.
         */
        public Object getValue() {
            return _value;
        }

        /**
         * Sets the value for this entry.
         * 
         * @param value the new value.
         * @return the previous value.
         */
        public Object setValue(Object value) {
            Object old = _value;
            _value = value;
            return old;
        }

        /**
         * Indicates if this entry is considered equals to the specified 
         * entry.
         * 
         * @param that the object to test for equality.
         * @return <code>true<code> if both entry are considered equal;
         *         <code>false<code> otherwise.
         */
        public boolean equals(Object that) {
            if (that instanceof Entry) {
                Entry entry = (Entry) that;
                return (_key.equals(entry.getKey()))
                        && ((_value != null) ? _value.equals(entry.getValue()) : (entry.getValue() == null));
            } else {
                return false;
            }
        }

        /**
         * Returns the hash code for this entry.
         * 
         * @return this entry's hash code.
         */
        public int hashCode() {
            return _key.hashCode() ^ ((_value != null) ? _value.hashCode() : 0);
        }

        /**
         * Returns the text representation of this entry.
         * 
         * @return this entry's textual representation.
         */
        public String toString() {
            return _key + "=" + _value;
        }
    }
}