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

Java tutorial

Introduction

Here is the source code for gnu.trove.map.custom_hash.TObjectCharCustomHashMap.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.TCharCollection;
import gnu.trove.function.TCharFunction;
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.TCharIterator;
import gnu.trove.iterator.TObjectCharIterator;
import gnu.trove.iterator.hash.TObjectHashIterator;
import gnu.trove.map.TObjectCharMap;
import gnu.trove.procedure.TCharProcedure;
import gnu.trove.procedure.TObjectCharProcedure;
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 char values.
 *
 * @author Rob Eden
 */
public class TObjectCharCustomHashMap<K> extends TCustomObjectHash<K> implements TObjectCharMap<K>, Externalizable {

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

    static final long serialVersionUID = 1L;

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

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

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

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

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

    /**
     * Creates a new <code>TObjectCharHashMap</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 TObjectCharCustomHashMap(HashingStrategy<K> strategy, int initialCapacity) {
        super(strategy, initialCapacity);

        no_entry_value = Constants.DEFAULT_CHAR_NO_ENTRY_VALUE;
    }

    /**
     * Creates a new <code>TObjectCharHashMap</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 TObjectCharCustomHashMap(HashingStrategy<K> strategy, int initialCapacity, float loadFactor) {

        super(strategy, initialCapacity, loadFactor);

        no_entry_value = Constants.DEFAULT_CHAR_NO_ENTRY_VALUE;
    }

    /**
     * Creates a new <code>TObjectCharHashMap</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 TObjectCharCustomHashMap(HashingStrategy<K> strategy, int initialCapacity, float loadFactor,
            char noEntryValue) {

        super(strategy, initialCapacity, loadFactor);

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

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

        if (map instanceof TObjectCharCustomHashMap) {
            TObjectCharCustomHashMap hashmap = (TObjectCharCustomHashMap) map;
            this._loadFactor = hashmap._loadFactor;
            this.no_entry_value = hashmap.no_entry_value;
            this.strategy = hashmap.strategy;
            //noinspection RedundantCast
            if (this.no_entry_value != (char) 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 char[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;
        char oldVals[] = _values;

        _set = new Object[newCapacity];
        Arrays.fill(_set, FREE);
        _values = new char[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 char getNoEntryValue() {
        return no_entry_value;
    }

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

    /** {@inheritDoc} */
    public boolean containsValue(char val) {
        Object[] keys = _set;
        char[] 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 char get(Object key) {
        int index = index(key);
        return index < 0 ? no_entry_value : _values[index];
    }

    // Modification Operations

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

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

    private char doPut(K key, char value, int index) {
        char 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 char remove(Object key) {
        char 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 Character> map) {
        Set<? extends Map.Entry<? extends K, ? extends Character>> set = map.entrySet();
        for (Map.Entry<? extends K, ? extends Character> entry : set) {
            put(entry.getKey(), entry.getValue());
        }
    }

    /** {@inheritDoc} */
    public void putAll(TObjectCharMap<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 TCharCollection valueCollection() {
        return new TCharValueCollection();
    }

    /** {@inheritDoc} */
    public char[] values() {
        char[] vals = new char[size()];
        char[] 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 char[] values(char[] array) {
        int size = size();
        if (array.length < size) {
            array = new char[size];
        }

        char[] 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 TObjectCharIterator<K> iterator() {
        return new TObjectCharHashIterator<K>(this);
    }

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

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

    /** {@inheritDoc} */
    public char adjustOrPutValue(final K key, final char adjust_amount, final char put_amount) {
        int index = insertionIndex(key);
        final boolean isNewMapping;
        final char 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>TCharProcedure</code> value
     * @return false if the loop over the values terminated because
     * the procedure returned false for some value.
     */
    public boolean forEachValue(TCharProcedure procedure) {
        Object[] keys = _set;
        char[] 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>TOObjectCharProcedure</code> value
     * @return false if the loop over the entries terminated because
     * the procedure returned false for some entry.
     */
    @SuppressWarnings({ "unchecked" })
    public boolean forEachEntry(TObjectCharProcedure<K> procedure) {
        Object[] keys = _set;
        char[] 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(TObjectCharProcedure<K> procedure) {
        boolean modified = false;
        //noinspection unchecked
        K[] keys = (K[]) _set;
        char[] 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>TCharFunction</code> value
     */
    public void transformValues(TCharFunction function) {
        Object[] keys = _set;
        char[] 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 TObjectCharMap)) {
            return false;
        }
        TObjectCharMap that = (TObjectCharMap) other;
        if (that.size() != this.size()) {
            return false;
        }
        try {
            TObjectCharIterator iter = this.iterator();
            while (iter.hasNext()) {
                iter.advance();
                Object key = iter.key();
                char 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;
        char[] 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(TObjectCharCustomHashMap.this);
        }

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

        public boolean containsElement(K key) {
            return TObjectCharCustomHashMap.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() {
            TObjectCharCustomHashMap.this.clear();
        }

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

        public int size() {
            return TObjectCharCustomHashMap.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 TObjectCharCustomHashMap.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 TCharValueCollection implements TCharCollection {

        /** {@inheritDoc} */
        public TCharIterator iterator() {
            return new TObjectCharValueHashIterator();
        }

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

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

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

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

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

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

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

        /** {@inheritDoc} */
        public boolean remove(char entry) {
            char[] 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 Character) {
                    char ele = ((Character) element).charValue();
                    if (!TObjectCharCustomHashMap.this.containsValue(ele)) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
            return true;
        }

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

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

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

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

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

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

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

        /** {@inheritDoc} */
        public boolean retainAll(char[] array) {
            boolean changed = false;
            Arrays.sort(array);
            char[] 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 Character) {
                    char c = ((Character) element).charValue();
                    if (remove(c)) {
                        changed = true;
                    }
                }
            }
            return changed;
        }

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

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

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

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

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

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

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

        class TObjectCharValueHashIterator implements TCharIterator {

            protected THash _hash = TObjectCharCustomHashMap.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 */
            TObjectCharValueHashIterator() {
                _expectedSize = _hash.size();
                _index = _hash.capacity();
            }

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

            /** {@inheritDoc} */
            public char 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();
                    TObjectCharCustomHashMap.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 = TObjectCharCustomHashMap.this._set;
                int i = _index;
                while (i-- > 0 && (set[i] == TCustomObjectHash.FREE || set[i] == TCustomObjectHash.REMOVED)) {
                    ;
                }
                return i;
            }
        }
    }

    class TObjectCharHashIterator<K> extends TObjectHashIterator<K> implements TObjectCharIterator<K> {

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

        public TObjectCharHashIterator(TObjectCharCustomHashMap<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 char value() {
            return _map._values[_index];
        }

        /** {@inheritDoc} */
        public char setValue(char val) {
            char 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.writeChar(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.writeChar(_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.readChar();

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

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

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

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

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