gnu.trove.map.custom_hash.TObjectDoubleCustomHashMap.java Source code

Java tutorial

Introduction

Here is the source code for gnu.trove.map.custom_hash.TObjectDoubleCustomHashMap.java

Source

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
// Copyright (c) 2009, Rob Eden All Rights Reserved.
// Copyright (c) 2009, Jeff Randall All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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 for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////

package gnu.trove.map.custom_hash;

import gnu.trove.TDoubleCollection;
import gnu.trove.function.TDoubleFunction;
import gnu.trove.impl.Constants;
import gnu.trove.impl.HashFunctions;
import gnu.trove.impl.hash.TCustomObjectHash;
import gnu.trove.impl.hash.THash;
import gnu.trove.iterator.TDoubleIterator;
import gnu.trove.iterator.TObjectDoubleIterator;
import gnu.trove.iterator.hash.TObjectHashIterator;
import gnu.trove.map.TObjectDoubleMap;
import gnu.trove.procedure.TDoubleProcedure;
import gnu.trove.procedure.TObjectDoubleProcedure;
import gnu.trove.procedure.TObjectProcedure;
import gnu.trove.strategy.HashingStrategy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.util.*;

//////////////////////////////////////////////////
// THIS IS A GENERATED CLASS. DO NOT HAND EDIT! //
//////////////////////////////////////////////////

/**
 * An open addressed Map implementation for Object keys and double values.
 *
 * @author Rob Eden
 */
public class TObjectDoubleCustomHashMap<K> extends TCustomObjectHash<K>
        implements TObjectDoubleMap<K>, Externalizable {

    private static final Log logger = LogFactory.getLog(TObjectDoubleCustomHashMap.class);

    static final long serialVersionUID = 1L;

    private final TObjectDoubleProcedure<K> PUT_ALL_PROC = new TObjectDoubleProcedure<K>() {
        public boolean execute(K key, double value) {
            put(key, value);
            return true;
        }
    };

    /** the values of the map */
    protected transient double[] _values;

    /** the value that represents null */
    protected double no_entry_value;

    /** FOR EXTERNALIZATION ONLY!!! */
    public TObjectDoubleCustomHashMap() {
    }

    /**
     * Creates a new <code>TObjectDoubleHashMap</code> instance with the default
     * capacity and load factor.
     */
    public TObjectDoubleCustomHashMap(HashingStrategy<K> strategy) {
        super(strategy);
    }

    /**
     * Creates a new <code>TObjectDoubleHashMap</code> instance with a prime
     * capacity equal to or greater than <tt>initialCapacity</tt> and
     * with the default load factor.
     *
     * @param initialCapacity an <code>int</code> value
     */
    public TObjectDoubleCustomHashMap(HashingStrategy<K> strategy, int initialCapacity) {
        super(strategy, initialCapacity);

        no_entry_value = Constants.DEFAULT_DOUBLE_NO_ENTRY_VALUE;
    }

    /**
     * Creates a new <code>TObjectDoubleHashMap</code> instance with a prime
     * capacity equal to or greater than <tt>initialCapacity</tt> and
     * with the specified load factor.
     *
     * @param initialCapacity an <code>int</code> value
     * @param loadFactor a <code>float</code> value
     */
    public TObjectDoubleCustomHashMap(HashingStrategy<K> strategy, int initialCapacity, float loadFactor) {

        super(strategy, initialCapacity, loadFactor);

        no_entry_value = Constants.DEFAULT_DOUBLE_NO_ENTRY_VALUE;
    }

    /**
     * Creates a new <code>TObjectDoubleHashMap</code> instance with a prime
     * value at or near the specified capacity and load factor.
     *
     * @param initialCapacity used to find a prime capacity for the table.
     * @param loadFactor used to calculate the threshold over which
     * rehashing takes place.
     * @param noEntryValue the value used to represent null.
     */
    public TObjectDoubleCustomHashMap(HashingStrategy<K> strategy, int initialCapacity, float loadFactor,
            double noEntryValue) {

        super(strategy, initialCapacity, loadFactor);

        no_entry_value = noEntryValue;
        //noinspection RedundantCast
        if (no_entry_value != (double) 0) {
            Arrays.fill(_values, no_entry_value);
        }
    }

    /**
     * Creates a new <code>TObjectDoubleCustomHashMap</code> that contains the entries
     * in the map passed to it.
     *
     * @param map the <tt>TObjectDoubleMap</tt> to be copied.
     */
    public TObjectDoubleCustomHashMap(HashingStrategy<K> strategy, TObjectDoubleMap<K> map) {
        this(strategy, map.size(), 0.5f, map.getNoEntryValue());

        if (map instanceof TObjectDoubleCustomHashMap) {
            TObjectDoubleCustomHashMap hashmap = (TObjectDoubleCustomHashMap) map;
            this._loadFactor = hashmap._loadFactor;
            this.no_entry_value = hashmap.no_entry_value;
            this.strategy = hashmap.strategy;
            //noinspection RedundantCast
            if (this.no_entry_value != (double) 0) {
                Arrays.fill(_values, this.no_entry_value);
            }
            setUp((int) Math.ceil(DEFAULT_CAPACITY / _loadFactor));
        }
        putAll(map);
    }

    /**
     * initializes the hashtable to a prime capacity which is at least
     * <tt>initialCapacity + 1</tt>.
     *
     * @param initialCapacity an <code>int</code> value
     * @return the actual capacity chosen
     */
    public int setUp(int initialCapacity) {
        int capacity;

        capacity = super.setUp(initialCapacity);
        _values = new double[capacity];
        return capacity;
    }

    /**
     * rehashes the map to the new capacity.
     *
     * @param newCapacity an <code>int</code> value
     */
    protected void rehash(int newCapacity) {
        int oldCapacity = _set.length;

        //noinspection unchecked
        K oldKeys[] = (K[]) _set;
        double oldVals[] = _values;

        _set = new Object[newCapacity];
        Arrays.fill(_set, FREE);
        _values = new double[newCapacity];
        Arrays.fill(_values, no_entry_value);

        for (int i = oldCapacity; i-- > 0;) {
            if (oldKeys[i] != FREE && oldKeys[i] != REMOVED) {
                K o = oldKeys[i];
                int index = insertionIndex(o);
                if (index < 0) {
                    throwObjectContractViolation(_set[(-index - 1)], o);
                }
                _set[index] = o;
                _values[index] = oldVals[i];
            }
        }
    }

    // Query Operations

    /** {@inheritDoc} */
    public double getNoEntryValue() {
        return no_entry_value;
    }

    /** {@inheritDoc} */
    public boolean containsKey(Object key) {
        return contains(key);
    }

    /** {@inheritDoc} */
    public boolean containsValue(double val) {
        Object[] keys = _set;
        double[] vals = _values;

        for (int i = vals.length; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED && val == vals[i]) {
                return true;
            }
        }
        return false;
    }

    /** {@inheritDoc} */
    public double get(Object key) {
        int index = index(key);
        return index < 0 ? no_entry_value : _values[index];
    }

    // Modification Operations

    /** {@inheritDoc} */
    public double put(K key, double value) {
        int index = insertionIndex(key);
        return doPut(key, value, index);
    }

    /** {@inheritDoc} */
    public double putIfAbsent(K key, double value) {
        int index = insertionIndex(key);
        if (index < 0)
            return _values[-index - 1];
        return doPut(key, value, index);
    }

    private double doPut(K key, double value, int index) {
        double previous = no_entry_value;
        boolean isNewMapping = true;
        if (index < 0) {
            index = -index - 1;
            previous = _values[index];
            isNewMapping = false;
        }
        //noinspection unchecked
        K oldKey = (K) _set[index];
        _set[index] = key;
        _values[index] = value;

        if (isNewMapping) {
            postInsertHook(oldKey == FREE);
        }
        return previous;
    }

    /** {@inheritDoc} */
    public double remove(Object key) {
        double prev = no_entry_value;
        int index = index(key);
        if (index >= 0) {
            prev = _values[index];
            removeAt(index); // clear key,state; adjust size
        }
        return prev;
    }

    /**
     * Removes the mapping at <tt>index</tt> from the map.
     * This method is used internally and public mainly because
     * of packaging reasons.  Caveat Programmer.
     *
     * @param index an <code>int</code> value
     */
    protected void removeAt(int index) {
        _values[index] = no_entry_value;
        super.removeAt(index); // clear key, state; adjust size
    }

    // Bulk Operations

    /** {@inheritDoc} */
    public void putAll(Map<? extends K, ? extends Double> map) {
        Set<? extends Map.Entry<? extends K, ? extends Double>> set = map.entrySet();
        for (Map.Entry<? extends K, ? extends Double> entry : set) {
            put(entry.getKey(), entry.getValue());
        }
    }

    /** {@inheritDoc} */
    public void putAll(TObjectDoubleMap<K> map) {
        map.forEachEntry(PUT_ALL_PROC);
    }

    /** {@inheritDoc} */
    public void clear() {
        super.clear();
        Arrays.fill(_set, 0, _set.length, FREE);
        Arrays.fill(_values, 0, _values.length, no_entry_value);
    }

    // Views

    /** {@inheritDoc} */
    public Set<K> keySet() {
        return new KeyView();
    }

    /** {@inheritDoc} */
    public Object[] keys() {
        //noinspection unchecked
        K[] keys = (K[]) new Object[size()];
        Object[] k = _set;

        for (int i = k.length, j = 0; i-- > 0;) {
            if (k[i] != FREE && k[i] != REMOVED) {
                //noinspection unchecked
                keys[j++] = (K) k[i];
            }
        }
        return keys;
    }

    /** {@inheritDoc} */
    public K[] keys(K[] a) {
        int size = size();
        if (a.length < size) {
            //noinspection unchecked
            a = (K[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
        }

        Object[] k = _set;

        for (int i = k.length, j = 0; i-- > 0;) {
            if (k[i] != FREE && k[i] != REMOVED) {
                //noinspection unchecked
                a[j++] = (K) k[i];
            }
        }
        return a;
    }

    /** {@inheritDoc} */
    public TDoubleCollection valueCollection() {
        return new TDoubleValueCollection();
    }

    /** {@inheritDoc} */
    public double[] values() {
        double[] vals = new double[size()];
        double[] v = _values;
        Object[] keys = _set;

        for (int i = v.length, j = 0; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED) {
                vals[j++] = v[i];
            }
        }
        return vals;
    }

    /** {@inheritDoc} */
    public double[] values(double[] array) {
        int size = size();
        if (array.length < size) {
            array = new double[size];
        }

        double[] v = _values;
        Object[] keys = _set;

        for (int i = v.length, j = 0; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED) {
                array[j++] = v[i];
            }
        }
        if (array.length > size) {
            array[size] = no_entry_value;
        }
        return array;
    }

    /**
     * @return an iterator over the entries in this map
     */
    public TObjectDoubleIterator<K> iterator() {
        return new TObjectDoubleHashIterator<K>(this);
    }

    /** {@inheritDoc} */
    @SuppressWarnings({ "RedundantCast" })
    public boolean increment(K key) {
        //noinspection RedundantCast
        return adjustValue(key, (double) 1);
    }

    /** {@inheritDoc} */
    public boolean adjustValue(K key, double amount) {
        int index = index(key);
        if (index < 0) {
            return false;
        } else {
            _values[index] += amount;
            return true;
        }
    }

    /** {@inheritDoc} */
    public double adjustOrPutValue(final K key, final double adjust_amount, final double put_amount) {
        int index = insertionIndex(key);
        final boolean isNewMapping;
        final double newValue;
        if (index < 0) {
            index = -index - 1;
            newValue = (_values[index] += adjust_amount);
            isNewMapping = false;
        } else {
            newValue = (_values[index] = put_amount);
            isNewMapping = true;
        }

        //noinspection unchecked
        K oldKey = (K) _set[index];
        _set[index] = key;

        if (isNewMapping) {
            postInsertHook(oldKey == FREE);
        }

        return newValue;
    }

    /**
     * Executes <tt>procedure</tt> for each key in the map.
     *
     * @param procedure a <code>TObjectProcedure</code> value
     * @return false if the loop over the keys terminated because
     * the procedure returned false for some key.
     */
    public boolean forEachKey(TObjectProcedure<K> procedure) {
        return forEach(procedure);
    }

    /**
     * Executes <tt>procedure</tt> for each value in the map.
     *
     * @param procedure a <code>TDoubleProcedure</code> value
     * @return false if the loop over the values terminated because
     * the procedure returned false for some value.
     */
    public boolean forEachValue(TDoubleProcedure procedure) {
        Object[] keys = _set;
        double[] values = _values;
        for (int i = values.length; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED && !procedure.execute(values[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Executes <tt>procedure</tt> for each key/value entry in the
     * map.
     *
     * @param procedure a <code>TOObjectDoubleProcedure</code> value
     * @return false if the loop over the entries terminated because
     * the procedure returned false for some entry.
     */
    @SuppressWarnings({ "unchecked" })
    public boolean forEachEntry(TObjectDoubleProcedure<K> procedure) {
        Object[] keys = _set;
        double[] values = _values;
        for (int i = keys.length; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED && !procedure.execute((K) keys[i], values[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Retains only those entries in the map for which the procedure
     * returns a true value.
     *
     * @param procedure determines which entries to keep
     * @return true if the map was modified.
     */
    public boolean retainEntries(TObjectDoubleProcedure<K> procedure) {
        boolean modified = false;
        //noinspection unchecked
        K[] keys = (K[]) _set;
        double[] values = _values;

        // Temporarily disable compaction. This is a fix for bug #1738760
        tempDisableAutoCompaction();
        try {
            for (int i = keys.length; i-- > 0;) {
                if (keys[i] != FREE && keys[i] != REMOVED && !procedure.execute(keys[i], values[i])) {
                    removeAt(i);
                    modified = true;
                }
            }
        } finally {
            reenableAutoCompaction(true);
        }

        return modified;
    }

    /**
     * Transform the values in this map using <tt>function</tt>.
     *
     * @param function a <code>TDoubleFunction</code> value
     */
    public void transformValues(TDoubleFunction function) {
        Object[] keys = _set;
        double[] values = _values;
        for (int i = values.length; i-- > 0;) {
            if (keys[i] != null && keys[i] != REMOVED) {
                values[i] = function.execute(values[i]);
            }
        }
    }

    // Comparison and hashing

    /**
     * Compares this map with another map for equality of their stored
     * entries.
     *
     * @param other an <code>Object</code> value
     * @return a <code>boolean</code> value
     */
    public boolean equals(Object other) {
        if (!(other instanceof TObjectDoubleMap)) {
            return false;
        }
        TObjectDoubleMap that = (TObjectDoubleMap) other;
        if (that.size() != this.size()) {
            return false;
        }
        try {
            TObjectDoubleIterator iter = this.iterator();
            while (iter.hasNext()) {
                iter.advance();
                Object key = iter.key();
                double value = iter.value();
                if (value == no_entry_value) {
                    if (!(that.get(key) == that.getNoEntryValue() && that.containsKey(key))) {
                        return false;
                    }
                } else {
                    if (value != that.get(key)) {
                        return false;
                    }
                }
            }
        } catch (ClassCastException ex) {
            // unused.
            logger.warn("An error occurred!", ex);
        }
        return true;
    }

    /** {@inheritDoc} */
    public int hashCode() {
        int hashcode = 0;
        Object[] keys = _set;
        double[] values = _values;
        for (int i = values.length; i-- > 0;) {
            if (keys[i] != FREE && keys[i] != REMOVED) {
                hashcode += HashFunctions.hash(values[i]) ^ (keys[i] == null ? 0 : keys[i].hashCode());
            }
        }
        return hashcode;
    }

    /** a view onto the keys of the map. */
    protected class KeyView extends MapBackedView<K> {

        @SuppressWarnings({ "unchecked" })
        public Iterator<K> iterator() {
            return new TObjectHashIterator(TObjectDoubleCustomHashMap.this);
        }

        public boolean removeElement(K key) {
            return no_entry_value != TObjectDoubleCustomHashMap.this.remove(key);
        }

        public boolean containsElement(K key) {
            return TObjectDoubleCustomHashMap.this.contains(key);
        }
    }

    private abstract class MapBackedView<E> extends AbstractSet<E> implements Set<E>, Iterable<E> {

        public abstract boolean removeElement(E key);

        public abstract boolean containsElement(E key);

        @SuppressWarnings({ "unchecked" })
        public boolean contains(Object key) {
            return containsElement((E) key);
        }

        @SuppressWarnings({ "unchecked" })
        public boolean remove(Object o) {
            return removeElement((E) o);
        }

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

        public boolean add(E obj) {
            throw new UnsupportedOperationException();
        }

        public int size() {
            return TObjectDoubleCustomHashMap.this.size();
        }

        public Object[] toArray() {
            Object[] result = new Object[size()];
            Iterator<E> e = iterator();
            for (int i = 0; e.hasNext(); i++) {
                result[i] = e.next();
            }
            return result;
        }

        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size) {
                //noinspection unchecked
                a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
            }

            Iterator<E> it = iterator();
            Object[] result = a;
            for (int i = 0; i < size; i++) {
                result[i] = it.next();
            }

            if (a.length > size) {
                a[size] = null;
            }

            return a;
        }

        public boolean isEmpty() {
            return TObjectDoubleCustomHashMap.this.isEmpty();
        }

        public boolean addAll(Collection<? extends E> collection) {
            throw new UnsupportedOperationException();
        }

        @SuppressWarnings({ "SuspiciousMethodCalls" })
        public boolean retainAll(Collection<?> collection) {
            boolean changed = false;
            Iterator<E> i = iterator();
            while (i.hasNext()) {
                if (!collection.contains(i.next())) {
                    i.remove();
                    changed = true;
                }
            }
            return changed;
        }
    }

    class TDoubleValueCollection implements TDoubleCollection {

        /** {@inheritDoc} */
        public TDoubleIterator iterator() {
            return new TObjectDoubleValueHashIterator();
        }

        /** {@inheritDoc} */
        public double getNoEntryValue() {
            return no_entry_value;
        }

        /** {@inheritDoc} */
        public int size() {
            return _size;
        }

        /** {@inheritDoc} */
        public boolean isEmpty() {
            return 0 == _size;
        }

        /** {@inheritDoc} */
        public boolean contains(double entry) {
            return TObjectDoubleCustomHashMap.this.containsValue(entry);
        }

        /** {@inheritDoc} */
        public double[] toArray() {
            return TObjectDoubleCustomHashMap.this.values();
        }

        /** {@inheritDoc} */
        public double[] toArray(double[] dest) {
            return TObjectDoubleCustomHashMap.this.values(dest);
        }

        public boolean add(double entry) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public boolean remove(double entry) {
            double[] values = _values;
            Object[] set = _set;

            for (int i = values.length; i-- > 0;) {
                if ((set[i] != FREE && set[i] != REMOVED) && entry == values[i]) {
                    removeAt(i);
                    return true;
                }
            }
            return false;
        }

        /** {@inheritDoc} */
        public boolean containsAll(Collection<?> collection) {
            for (Object element : collection) {
                if (element instanceof Double) {
                    double ele = ((Double) element).doubleValue();
                    if (!TObjectDoubleCustomHashMap.this.containsValue(ele)) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
            return true;
        }

        /** {@inheritDoc} */
        public boolean containsAll(TDoubleCollection collection) {
            TDoubleIterator iter = collection.iterator();
            while (iter.hasNext()) {
                if (!TObjectDoubleCustomHashMap.this.containsValue(iter.next())) {
                    return false;
                }
            }
            return true;
        }

        /** {@inheritDoc} */
        public boolean containsAll(double[] array) {
            for (double element : array) {
                if (!TObjectDoubleCustomHashMap.this.containsValue(element)) {
                    return false;
                }
            }
            return true;
        }

        /** {@inheritDoc} */
        public boolean addAll(Collection<? extends Double> collection) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public boolean addAll(TDoubleCollection collection) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        public boolean addAll(double[] array) {
            throw new UnsupportedOperationException();
        }

        /** {@inheritDoc} */
        @SuppressWarnings({ "SuspiciousMethodCalls" })
        public boolean retainAll(Collection<?> collection) {
            boolean modified = false;
            TDoubleIterator iter = iterator();
            while (iter.hasNext()) {
                if (!collection.contains(Double.valueOf(iter.next()))) {
                    iter.remove();
                    modified = true;
                }
            }
            return modified;
        }

        /** {@inheritDoc} */
        public boolean retainAll(TDoubleCollection collection) {
            if (this == collection) {
                return false;
            }
            boolean modified = false;
            TDoubleIterator iter = iterator();
            while (iter.hasNext()) {
                if (!collection.contains(iter.next())) {
                    iter.remove();
                    modified = true;
                }
            }
            return modified;
        }

        /** {@inheritDoc} */
        public boolean retainAll(double[] array) {
            boolean changed = false;
            Arrays.sort(array);
            double[] values = _values;

            Object[] set = _set;
            for (int i = set.length; i-- > 0;) {
                if (set[i] != FREE && set[i] != REMOVED && (Arrays.binarySearch(array, values[i]) < 0)) {
                    removeAt(i);
                    changed = true;
                }
            }
            return changed;
        }

        /** {@inheritDoc} */
        public boolean removeAll(Collection<?> collection) {
            boolean changed = false;
            for (Object element : collection) {
                if (element instanceof Double) {
                    double c = ((Double) element).doubleValue();
                    if (remove(c)) {
                        changed = true;
                    }
                }
            }
            return changed;
        }

        /** {@inheritDoc} */
        public boolean removeAll(TDoubleCollection collection) {
            if (this == collection) {
                clear();
                return true;
            }
            boolean changed = false;
            TDoubleIterator iter = collection.iterator();
            while (iter.hasNext()) {
                double element = iter.next();
                if (remove(element)) {
                    changed = true;
                }
            }
            return changed;
        }

        /** {@inheritDoc} */
        public boolean removeAll(double[] array) {
            boolean changed = false;
            for (int i = array.length; i-- > 0;) {
                if (remove(array[i])) {
                    changed = true;
                }
            }
            return changed;
        }

        /** {@inheritDoc} */
        public void clear() {
            TObjectDoubleCustomHashMap.this.clear();
        }

        /** {@inheritDoc} */
        public boolean forEach(TDoubleProcedure procedure) {
            return TObjectDoubleCustomHashMap.this.forEachValue(procedure);
        }

        @Override
        public String toString() {
            final StringBuilder buf = new StringBuilder("{");
            forEachValue(new TDoubleProcedure() {
                private boolean first = true;

                public boolean execute(double value) {
                    if (first) {
                        first = false;
                    } else {
                        buf.append(", ");
                    }

                    buf.append(value);
                    return true;
                }
            });
            buf.append("}");
            return buf.toString();
        }

        class TObjectDoubleValueHashIterator implements TDoubleIterator {

            protected THash _hash = TObjectDoubleCustomHashMap.this;

            /**
             * the number of elements this iterator believes are in the
             * data structure it accesses.
             */
            protected int _expectedSize;

            /** the index used for iteration. */
            protected int _index;

            /** Creates an iterator over the specified map */
            TObjectDoubleValueHashIterator() {
                _expectedSize = _hash.size();
                _index = _hash.capacity();
            }

            /** {@inheritDoc} */
            public boolean hasNext() {
                return nextIndex() >= 0;
            }

            /** {@inheritDoc} */
            public double next() {
                moveToNextIndex();
                return _values[_index];
            }

            /** @{inheritDoc} */
            public void remove() {
                if (_expectedSize != _hash.size()) {
                    throw new ConcurrentModificationException();
                }

                // Disable auto compaction during the remove. This is a workaround for bug 1642768.
                try {
                    _hash.tempDisableAutoCompaction();
                    TObjectDoubleCustomHashMap.this.removeAt(_index);
                } finally {
                    _hash.reenableAutoCompaction(false);
                }

                _expectedSize--;
            }

            /**
             * Sets the internal <tt>index</tt> so that the `next' object
             * can be returned.
             */
            protected final void moveToNextIndex() {
                // doing the assignment && < 0 in one line shaves
                // 3 opcodes...
                if ((_index = nextIndex()) < 0) {
                    throw new NoSuchElementException();
                }
            }

            /**
             * Returns the index of the next value in the data structure
             * or a negative value if the iterator is exhausted.
             *
             * @return an <code>int</code> value
             * @throws java.util.ConcurrentModificationException
             *          if the underlying
             *          collection's size has been modified since the iterator was
             *          created.
             */
            protected final int nextIndex() {
                if (_expectedSize != _hash.size()) {
                    throw new ConcurrentModificationException();
                }

                Object[] set = TObjectDoubleCustomHashMap.this._set;
                int i = _index;
                while (i-- > 0 && (set[i] == TCustomObjectHash.FREE || set[i] == TCustomObjectHash.REMOVED)) {
                    ;
                }
                return i;
            }
        }
    }

    class TObjectDoubleHashIterator<K> extends TObjectHashIterator<K> implements TObjectDoubleIterator<K> {

        /** the collection being iterated over */
        private final TObjectDoubleCustomHashMap<K> _map;

        public TObjectDoubleHashIterator(TObjectDoubleCustomHashMap<K> map) {
            super(map);
            this._map = map;
        }

        /** {@inheritDoc} */
        public void advance() {
            moveToNextIndex();
        }

        /** {@inheritDoc} */
        @SuppressWarnings({ "unchecked" })
        public K key() {
            return (K) _map._set[_index];
        }

        /** {@inheritDoc} */
        public double value() {
            return _map._values[_index];
        }

        /** {@inheritDoc} */
        public double setValue(double val) {
            double old = value();
            _map._values[_index] = val;
            return old;
        }
    }

    // Externalization

    public void writeExternal(ObjectOutput out) throws IOException {
        // VERSION
        out.writeByte(0);

        // SUPER
        super.writeExternal(out);

        // STRATEGY
        out.writeObject(strategy);

        // NO_ENTRY_VALUE
        out.writeDouble(no_entry_value);

        // NUMBER OF ENTRIES
        out.writeInt(_size);

        // ENTRIES
        for (int i = _set.length; i-- > 0;) {
            if (_set[i] != REMOVED && _set[i] != FREE) {
                out.writeObject(_set[i]);
                out.writeDouble(_values[i]);
            }
        }
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

        // VERSION
        in.readByte();

        // SUPER
        super.readExternal(in);

        // STRATEGY
        strategy = (HashingStrategy<K>) in.readObject();

        // NO_ENTRY_VALUE
        no_entry_value = in.readDouble();

        // NUMBER OF ENTRIES
        int size = in.readInt();
        setUp(size);

        // ENTRIES
        while (size-- > 0) {
            //noinspection unchecked
            K key = (K) in.readObject();
            double val = in.readDouble();
            put(key, val);
        }
    }

    /** {@inheritDoc} */
    public String toString() {
        final StringBuilder buf = new StringBuilder("{");
        forEachEntry(new TObjectDoubleProcedure<K>() {
            private boolean first = true;

            public boolean execute(K key, double value) {
                if (first)
                    first = false;
                else
                    buf.append(",");

                buf.append(key).append("=").append(value);
                return true;
            }
        });
        buf.append("}");
        return buf.toString();
    }
}