Package ariba.util.core

Source Code of ariba.util.core.Hashtable$HashtableIterator

/*
    Copyright 1996-2008 Ariba, Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

    $Id: //ariba/platform/util/core/ariba/util/core/Hashtable.java#18 $
*/

package ariba.util.core;

import ariba.util.io.FormattingSerializer;
import ariba.util.io.Serializer;
import ariba.util.log.Log;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

    // There needs to be a way to call Object.hashCode() directly when you want
    // to do identity hashing.  ALERT!  (What does this mean? -- mhb 7/12/97)

    // there is now an eqHashtable for just this. In addition there is
    // a hash table that only grows but does not require
    // synchronization on gets and provides internal synchronization
    // on puts. Much of the code is (necessarially) duplicated. If
    // there are any bugs in this Hashtable, it would be worth
    // checking the GrowOnlyHashtable to see if the same bug exists.
    // --ds 12/21/98


/**
    A Hashtable class that has a number of performance improvements
    over other Hashtable classes. The methods are not internally
    synchronized, so care must be taken at the application level to
    synchronize.

    @aribaapi documented
*/
public class Hashtable extends AbstractMap
        implements Cloneable, Externalizable
{

    private static final int linearSearchThreshold = 16;
    private static final Object emptyArray[] = new Object[0];

    /**
        For the multiplicative hash, choose the golden ratio:
        <pre>
        A = ((sqrt(5) - 1) / 2) * (1 << 32)
        </pre>
        ala Knuth...

        @aribaapi private
    */
    protected static final int A = 0x9e3779b9;

    /**
        Name of field for keys.  Value is "keys".

        @aribaapi private
    */
    protected static final String keysField = "keys";

    /**
        Name of field for elements.  Value is "elements".

        @aribaapi private
    */
    protected static final String elementsField = "elements";

    /**
        Default Hashtable size.  Value is 32.

        @aribaapi private
    */
    protected static final int DefaultSize = 32;

    /**
        Placeholder object that represents a deleted entry.

        @aribaapi private
    */
    protected static final Object DeletedMarker = new Object();

    /**
        Number of elements currently in Hashtable.

        @aribaapi private
    */
    protected int count;

    /**
        Total number of occupied slots.

        @aribaapi private
    */
    protected int totalCount;

    protected int shift;

    /**
        Hashtable capacity.

        @aribaapi private
    */
    protected int capacity;

    protected int indexMask;

    /**
        Hashtable keys.

        @aribaapi private
    */
    protected Object keys[];

    /**
        Hashtable elements.

        @aribaapi private
    */
    protected Object elements[];

    /**
        Constructs an empty Hashtable. The Hashtable will grow on demand
        as more elements are added.
        @aribaapi documented
        @deprecated use MapUtil.map
        @see ariba.util.core.MapUtil#map()
    */
    public Hashtable ()
    {
        super();
        this.shift = 32 - MathUtil.log2(DefaultSize) + 1;
    }

    /**
        Constructs a Hashtable capable of holding a specified number
        of elements.

        @param initialCapacity the number of elements this Hashtable
        is capable of holding before needing to grow.
        @aribaapi documented
        @deprecated use MapUtil.map
        @see ariba.util.core.MapUtil#map(int)
    */
    public Hashtable (int initialCapacity)
    {
        this();

        if (initialCapacity < 0) {
            throw new IllegalArgumentException("initialCapacity must be >= 0");
        }

        grow(initialCapacity);
    }

    /**
        Creates new Hashtable with the same contents as the given Hashtable.

        @param source source Hashtable
        @aribaapi private
        @deprecated use MapUtil.map
        @see ariba.util.core.MapUtil#map(java.util.Map)
    */
    public Hashtable (java.util.Hashtable source)
    {
        this(source.size());
        Enumeration k = source.keys();
        Enumeration e = source.elements();
        while (k.hasMoreElements()) {
            put(k.nextElement(), e.nextElement());
        }
    }

    /**
        Creates new Hashtable with the same contents as the given Map.

        @param source source Map
        @aribaapi private
    */
    public Hashtable (Map source)
    {
        this(source.size());
        putAll(source);
    }

    /**
        Creates a shallow copy of the Hashtable. The table itself is cloned,
        but none of the keys or elements are copied.

        @return the cloned copy

        @throws InternalError if the cloning operation fails.

        @aribaapi documented
    */
    public Object clone ()
    {
        Hashtable newTable;
        try {
            newTable = (Hashtable)super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new InternalError("Error in clone(). This shouldn't happen.");
        }

            // don't try to share cached collection classes across
            // cloned instances of hashtables.
        newTable.keySet = null;
        newTable.values = null;
       
        // If there is nothing in the table, then we cloned to make sure the
        // class was preserved, but just null everything out except for shift,
        // which implies the default initial capacity.

        if (count == 0) {
            newTable.shift = 32 - MathUtil.log2(DefaultSize) + 1;
            newTable.totalCount = 0;
            newTable.capacity = 0;
            newTable.indexMask = 0;
            newTable.keys = null;
            newTable.elements = null;

            return newTable;
        }

        int len = keys.length;
        newTable.keys = new Object[len];
        newTable.elements = new Object[len];

        System.arraycopy(keys, 0, newTable.keys, 0, len);
        System.arraycopy(elements, 0, newTable.elements, 0, len);

        return newTable;
    }

    /**
        Returns the number of elements in the Hashtable.
        @return the number of elements in the Hashtable
        @aribaapi ariba
        @deprecated replaced by size
        @see #size
    */
    public int count ()
    {
        return count;
    }

    /**
        Returns the number of elements in the Hashtable.
        @return the number of elements in the Hashtable
        @aribaapi documented
    */
    public int size ()
    {
        return count;
    }

    /**
        Indicates if the hashtable contains any elements.
        @return <b>true</b> if there are no elements in the Hashtable.
        @aribaapi ariba
    */
    public boolean isEmpty ()
    {
        return (count == 0);
    }

    /**
        Returns an Enumeration of the Hashtable's keys.

        @return an enumeration of the keys in this Hashtable.
        @deprecated use keySet.iterator() instead
        @see #keySet
        @see java.util.Set#iterator
        @see #elements()
        @aribaapi ariba
    */
    public Enumeration keys ()
    {
        return new HashtableEnumerator(this, true);
    }

    /**
        Returns an Enumeration of the Hashtable's elements.

        @return an enumeration of the elements in this Hashtable.
        @deprecated use values.iterator() instead
        @see #values
        @see java.util.Collection#iterator
        @see #keys()
        @aribaapi documented
    */
    public Enumeration elements ()
    {
        return new HashtableEnumerator(this, false);
    }

    /**
        Returns a Vector containing the Hashtable's keys. If the Hashtable
        contains no keys, return an empty Vector object.

        @return the Vector object mentioned above.
        @deprecated use MapUtil.keysList
        @see ariba.util.core.MapUtil#keysList
        @aribaapi ariba
    */
    public Vector keysVector ()
    {
        if (count == 0) {
            return new Vector();
        }

        Vector vect = new Vector(count);
        int vectCount = 0;

        for (int i = 0; i < keys.length && vectCount < count; i++) {
            Object key = keys[i];
            if (key != null && key != DeletedMarker) {
                vect.addElement(key);
                vectCount++;
            }
        }

        return vect;
    }

    /**
        Returns a Vector containing the Hashtable's elements. If the hashtable
        containes no elements, an empty Vector object is returned.
        @return the Vector object mentioned above.
        @deprecated use MapUtil.elementsList
        @see ariba.util.core.MapUtil#elementsList
        @aribaapi ariba
    */
    public Vector elementsVector ()
    {
        if (count == 0) {
            return new Vector();
        }

        Vector vect = new Vector(count);
        int vectCount = 0;

        for (int i = 0; i < elements.length && vectCount < count; i++) {
            Object element = elements[i];
            if (element != null && element != DeletedMarker) {
                vect.addElement(element);
                vectCount++;
            }
        }

        return vect;
    }

    /**
        Returns an Object array containing the Hashtable's keys. If the
        Hashtable contains no keys, an empty array is returned.
        @return the Object array above mentioned.
        @deprecated use MapUtil.keysArray
        @see ariba.util.core.MapUtil#keysArray
        @aribaapi ariba
    */
    public Object[] keysArray ()
    {
        if (count == 0) {
            return emptyArray;
        }

        Object[] array = new Object[count];
        int arrayCount = 0;

        for (int i = 0; i < keys.length && arrayCount < count; i++) {
            Object key = keys[i];
            if (key != null && key != DeletedMarker) {
                array[arrayCount++] = key;
            }
        }

        return array;
    }
    /**
        Returns an Object array containing the Hashtable's elements.
        @return the Object array mentioned above. If there the hashtable contains
        no elements, an empty array is returned.
        @aribaapi ariba
        @deprecated use MapUtil.elementsArray
        @see ariba.util.core.MapUtil#elementsArray
    */
    public Object[] elementsArray ()
    {
        if (count == 0) {
            return emptyArray;
        }

        Object[] array = new Object[count];
        int arrayCount = 0;

        for (int i = 0; i < elements.length && arrayCount < count; i++) {
            Object element = elements[i];
            if (element != null && element != DeletedMarker) {
                array[arrayCount++] = element;
            }
        }

        return array;
    }

    /**
        Checks if the Hashtable contains the specified element.  This
        method is slow -- O(n) -- because it must scan the table
        searching for the element.

        @param element the element to search for in the Hashtable
        @exception NullPointerException if element is null

        @return Returns <b>true</b> if the Hashtable contains the
        element.
        @aribaapi ariba
    */
    public boolean contains (Object element)
    {
        // We need to short-circuit here since the data arrays may not have
        // been allocated yet.

        if (count == 0) {
            return false;
        }

        if (element == null) {
            throw new NullPointerException();
        }

        if (elements == null) {
            return false;
        }

        for (int i = 0; i < elements.length; i++) {
            Object tmp = elements[i];
            if (tmp != null && tmp != DeletedMarker && element.equals(tmp)) {
                return true;
            }
        }

        return false;
    }

    /**
        Checks if the Hashtable has an entry matching the key
        <b>key</b>.

        @param key the key to search for in the Hashtable
        @exception NullPointerException if key is null

        @return <b>true</b> if the Hashtable contains the key <b>key</b>.
        @aribaapi documented
    */
    public boolean containsKey (Object key)
    {
        return (get(key) != null);
    }

    /**
        Searches the Hashtable and returns the element associated with
        the matched key.

        @param key the key to search the hashtable's keys
        for. Hashtable hashes and compares <b>key</b> using
        <b>hashCode()</b> and <b>equals()</b>.

        @exception NullPointerException if <b>key</b> is null and this <b>Hashtable</b>
        is not empty.

        @return the element associated with the <b>key</b>. This
        method returns <b>null</b> if the Hashtable does not contain
        <b>key</b>.
        @aribaapi documented
    */
    public Object get (Object key)
    {
            // We need to short-circuit here since the data arrays may not have
            // been allocated yet.
        if (count == 0) {
            return null;
        }

        Object element = elements[tableIndexFor(key)];
        return (element == DeletedMarker? null : element;
    }

    /**
        Finds and removes <b>key</b> and the element associated with
        it from the Hashtable.

        @param key the key to search the hashtable's keys
        for. Hashtable hashes and compares <b>key</b> using
        <b>hashCode()</b> and <b>equals()</b>.

        @return the element associated with the <b>key</b> removed, or
        <b>null</b> if <b>key</b> was not present.
        @aribaapi documented
    */
    public Object remove (Object key)
    {
        if (key == null) {
                // map interface allows NPE, not FatalAssertException
            throw new NullPointerException();
        }

        // We need to short-circuit here since the data arrays may not have
        // been allocated yet.

        if (count == 0) {
            return null;
        }

        int index = tableIndexFor(key);
        Object oldValue = elements[index];
        if (oldValue == null || oldValue == DeletedMarker) {
            return null;
        }
        removeAt(index);

        return oldValue;
    }


    private void removeAt (int index)
    {
        if (keys.length <= linearSearchThreshold) {
            int lastActive = count-1;
            elements[index] = elements[lastActive];
            keys[index] = keys[lastActive];
            elements[lastActive] = null;
            keys[lastActive] = null;
        }
        else {
            keys[index] = DeletedMarker;
            elements[index] = DeletedMarker;
        }

        count--;
    }

    /**
        Finds and removes all occurrences of <b>element</b> from the Hashtable.
        Note that this method is slow -- O(n) -- because it must scan the table
        searching for the element.

        @param element the element to search for.  Hashtable compares elements
        using <b>equals()</b>.
        @deprecated use MapUtil.removeElement
        @see ariba.util.core.MapUtil#removeElement

        @return the number of elements removed.
        @aribaapi ariba
    */
    public int removeElement (Object element)
    {
        Vector removedKeys = keysForRemovedElement(element);
        return (removedKeys == null ? 0 : removedKeys.count());
    }

    /**
        Finds and removes all occurrences of <b>element</b> from the Hashtable.
        Note that this method is slow -- O(n) -- because it must scan the table
        searching for the element.

        @param element the element to search for.  Hashtable compares elements
        using <b>equals()</b>.

        @return a Vector containing the keys whose values are the element to be
        removed.
        @aribaapi private
    */
    protected Vector keysForRemovedElement (Object element)
    {
        // We need to short-circuit here since the data arrays may not have
        // been allocated yet.

        if (count == 0) {
            return null;
        }

        if (element == null) {
            throw new NullPointerException();
        }

        if (elements == null) {
            return null;
        }

        Vector removedKeys = new Vector();
        for (int i = 0; i < elements.length; i++) {
            Object tmp = elements[i];
            if (tmp != null && tmp != DeletedMarker && element.equals(tmp)) {
                removedKeys.add(keys[i]);
                if (keys.length <= linearSearchThreshold) {
                    int lastActive = count-1;
                    elements[i] = elements[lastActive];
                    keys[i] = keys[lastActive];
                    elements[lastActive] = null;
                    keys[lastActive] = null;
                        // trick so we re-process this slot
                    i--;
                }
                else {
                    keys[i] = DeletedMarker;
                    elements[i] = DeletedMarker;
                }
                count--;
            }
        }

        return removedKeys;
    }

    /**
        Places the <b>key</b>/<b>element</b> pair in the Hashtable. If
        there is a key in the Hashtable matching <b>key</b> the
        corresponding element in the Hashtable is replaced by the
        specified <b>element</b>

        @param key the key to place into the Hashtable; may not be
        null
        @param element the element to place into the Hashtable; may
        not be null

        @return the element previously associated with the key if any,
        <b>null</b> otherwise.

        @exception NullPointerException if <b>element</b> or
        <b>key</b> are null
        @aribaapi documented
    */
    public Object put (Object key, Object element)
    {
        if (element == null) {
            throw new NullPointerException();
        }

        // Since we delay allocating the data arrays until we actually need
        // them, check to make sure they exist before attempting to put
        // something in them.

        if (keys == null) {
            grow();
        }

        int index = tableIndexFor(key);
        Object oldValue = elements[index];

        // If the total number of occupied slots (either with a real element or
        // a removed marker) gets too big, grow the table.

        if (oldValue == null || oldValue == DeletedMarker) {
            if (oldValue != DeletedMarker) {
                if (totalCount >= capacity) {
                    grow();
                    return put(key, element);
                }
                totalCount++;
            }
            count++;
        }

        keys[index] = key;
        elements[index] = element;

        return (oldValue == DeletedMarker) ? null : oldValue;
    }

    /**
        Helper function that returns the appropriate hash code for the
        object <b>o</b>. Unless overriden by a subclass, this will
        return the object's hashCode().

        @param o object
        @return hash code for <code>o</code>
        @aribaapi private
    */
    protected int getHashValueForObject (Object o)
    {
        return o.hashCode();
    }

    /**
        Helper function to determine if two objects are equal. This
        method is overriden for different types of hash tables. Unless
        overriden by a subclass, it returns true if <b>obj1</b> and
        <b>obj2</b> compare as equal using the equals() method on
        <b>obj1</b>

        @param obj1 object to compare
        @param obj2 object to compare
        @return true if <b>obj1</b> and <b>obj2</b> compare as equal using the
                equals() method on <b>obj1</b>
        @aribaapi private
    */
    protected boolean objectsAreEqualEnough (Object obj1, Object obj2)
    {
            // Looking at the implementation of visualcafe 1.1 and sun
            // String.equals() isn't smart enough to avoid full
            // compares when strings are pointer eq. (HP does)
        if (obj1 == obj2) {
            return true;
        }
        return obj1.equals(obj2);
    }

    /**
        Primitive method used internally to find slots in the
        table. If the key is present in the table, this method will
        return the index under which it is stored. If the key is not
        present, then this method will return the index under which it
        can be put. The caller must look at the data at that index to
        differentiate between the two possibilities.

        @param key key
        @return index under which key is stored
        @aribaapi private
    */
    protected int tableIndexForLinearSearch (Object key)
    {
        for (int i = 0; i < count; i++) {
            if (keys[i] == key) {
                return i;
            }
        }
        for (int i = 0; i < count; i++) {
            Object tempKey = keys[i];
            if (tempKey == null) {
                Log.util.error(2755, i, count, keys.length);
                    // make half assed assumption that the key is not
                    // in the table, and return the supposed index of
                    // next element to use
                return count;
            }
            if (objectsAreEqualEnough(tempKey, key)) {
                return i;
            }
        }
        Assert.that(count <= elements.length, "Hashtable overflow");
        return count;
    }

    /**
        Primitive method used internally to find slots in the
        table. If the key is present in the table, this method will return the
        index
        under which it is stored. If the key is not present, then this
        method will return the index under which it can be put. The caller
        must look at the hashCode at that index to differentiate between
        the two possibilities.

        @param key key, may not be <b>null</b>
        @return index under which key is stored
        @exception NullPointerException if <b>key</b> is null
        @aribaapi private
    */
    private int tableIndexFor (Object key)
    {
        if (key == null) {
            throw new NullPointerException();           
        }
       
        if (keys.length <= linearSearchThreshold) {
            return tableIndexForLinearSearch(key);
        }
        int hash = getHashValueForObject(key);
        int product = hash * A;
        int index = product >>> shift;

        // Probe the first slot in the table.  We keep track of the first
        // index where we found a REMOVED marker so we can return that index
        // as the first available slot if the key is not already in the table.

        Object oldKey = keys[index];
        if (oldKey == null || objectsAreEqualEnough(key, oldKey)) {
            return index;
        }

        int removedIndex = (oldKey == DeletedMarker) ? index : -1;

        // Our first probe has failed, so now we need to start looking
        // elsewhere in the table.

        int step = ((product >>> (2 * shift - 32)) & indexMask) | 1;
        int probeCount = 1;
        do {
            probeCount++;
            index = (index + step) & indexMask;

            Object testKey = keys[index];
            if (testKey == null) {
                if (removedIndex < 0) {
                    return index;
                }
                return removedIndex;
            }
            if (objectsAreEqualEnough(key, testKey)) {
                return index;
            }
            if (testKey == DeletedMarker && removedIndex == -1) {
                removedIndex = index;
            }

        }
        while (probeCount <= totalCount);

        // Something very bad has happened.
        Assert.that(false, "Hashtable overflow");
        return -1;
    }

    /**
        Grows the table to accommodate at least <b>capacity</b> number
        of elements.

        @param capacity the minimum number of elements the Hashtable
        must be able to hold without growing
        @aribaapi private
    */
    private void grow (int capacity)
    {
        int tableSize, power;

        // Find the lowest power of 2 size for the table which will allow
        // us to insert initialCapacity number of objects before having to
        // grow.

        tableSize = (capacity * 4) / 3;

        power = 3;
        while ((1 << power) < tableSize)
            power++;

        // Once shift is set, then grow() will do the right thing when
        // called.

        shift = 32 - power + 1;
        grow();
    }

    /**
        Grows the table by a factor of 2 (or creates it if necessary).
        All the REMOVED markers go away and the elements are rehashed
        into the bigger table.
    */
    private void grow ()
    {
        // The table size needs to be a power of two, and it should double
        // when it grows.  We grow when we are more than 3/4 full.

        shift--;
        int power = 32 - shift;
        indexMask = (1 << power) - 1;
        capacity = (3 * (1 << power)) / 4;

        Object[] oldKeys = keys;
        Object[] oldValues = elements;

        keys = new Object[1 << power];
        elements = new Object[1 << power];

        // Reinsert the old elements into the new table if there are any.  Be
        // sure to reset the counts and increment them as the old entries are
        // put back in the table.

        totalCount = 0;

        if (count > 0) {
            count = 0;

            for (int i = 0; i < oldKeys.length; i++) {
                Object key = oldKeys[i];

                if (key != null && key != DeletedMarker) {
                    int index = tableIndexFor(key);

                    keys[index] = key;
                    elements[index] = oldValues[i];

                    count++;
                    totalCount++;
                }
            }
        }
    }

    /**
        Removes all keys and elements from the Hashtable.
        @aribaapi documented
    */
    public void clear ()
    {
        int i;

        if (keys == null) {
            return;
        }

        for (i = 0; i < keys.length; i++) {
            keys[i] = null;
            elements[i] = null;
        }

        count = 0;
        totalCount = 0;
    }

    /**
        Returns the Hashtable's string representation.
        @return the Hashtable's string representation
        @aribaapi documented
    */
    public String toString ()
    {
        return FormattingSerializer.serializeObject(this);
    }

    /**
        Converts a string into a string array for persistence purposes.

        @return string array serialization of this Hashtable
        @aribaapi private
    */
    public String[] toStringArray ()
    {
        return MapUtil.toStringArray(this);
    }

    /**
        Returns a string serialization of the Hashtable using the
        Serializer.

        @see Serializer

        @return string serialization of this Hashtable
        @aribaapi private
    */
    public String toSerializedString ()
    {
        return MapUtil.toSerializedString(this);
    }

    /**
        Populates the hashtable with serialized data from the string.

        @param serialized String containing serialized Hashtable data
        @aribaapi private
    */
    public void fromSerializedString (String serialized)
    {
        MapUtil.fromSerializedString(this, serialized);
    }

    /**
        Implementation of Externalizable interface.  Saves the contents of this
        Hashtable object.

        @serialData iterates through entries, calling writeObject() on each key
                    followed by each value

        @param output the stream to write the object to
        @exception IOException Includes any I/O exceptions that may occur
        @aribaapi private
    */
    public void writeExternal (ObjectOutput output) throws IOException
    {
        int count = count();
        output.writeInt(count);
        Enumeration keys = keys();
        while (keys.hasMoreElements()) {
            --count;
            Object key = keys.nextElement();
            if (key == null) {
                Assert.that(false, "Null key in hash table.");
            }
            output.writeObject(key);
            output.writeObject(get(key));
        }
        Assert.that(count == 0, "count is not equal to number of keys");
    }

    /**
        Implementation of Externalizable interface.  Reads the contents into
        this Hashtable.

        @param input the stream to read data from in order to restore the object
        @exception IOException if I/O errors occur
        @exception ClassNotFoundException If the class for an object being
                     restored cannot be found.
        @aribaapi private
    */
    public void readExternal (ObjectInput input)
      throws IOException, ClassNotFoundException
    {
        int count = input.readInt();
        for (int i = 0; i < count; i++) {
            put(input.readObject(), input.readObject());
        }
    }

        /****************************************
         Implementation of AbstractMap
        *****************************************/
    /**
        Note: While iterating through a set of entries in the Hashtable, do not
        keep references to Key.Entry objects after a subsequent call to next().
        Each entry is valid from the point where next() is called until
        the subsequent call to next(), at which point the old entry is invalid.

        Entries do remain valid after a call to remove(), however.

        @return a set of Key.Entry objects
        @aribaapi documented
    */
    public Set entrySet ()
    {
        return new HashtableEntrySet();
    }

    private class HashtableEntrySet extends AbstractSet
    {
        /**
            Returns an iterator over the elements contained in this collection.

            @return an iterator over the elements contained in this collection.
            @aribaapi private
        */
        public Iterator iterator ()
        {
            return new HashtableIterator(HashtableIterator.Entries);
        }

        /**
            Returns the number of elements in this collection.  If the collection
            contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
            <tt>Integer.MAX_VALUE</tt>.

            @return the number of elements in this collection.
            @aribaapi private
        */
        public int size ()
        {
            return Hashtable.this.size();
        }
    }

    private class HashtableEntry implements Map.Entry
    {
        private int _index = -1;
        private Object _key;
        private Object _value;

        /**
            Creates new HashtableEntry.

            @return new HashtableEntry
            @aribaapi private
        */
        public HashtableEntry ()
        {
        }

        /**
            Sets the index for this HashtableEntry.

            @param i index
            @aribaapi private
        */
        public void setIndex (int i)
        {
            _index = i;
            _key = Hashtable.this.keys[_index];
            _value = Hashtable.this.elements[_index];
        }

        /**
            Returns the key corresponding to this entry.

            @return the key corresponding to this entry.
            @aribaapi private
        */
        public Object getKey ()
        {
            return _key;
        }

        /**
            Returns the value corresponding to this entry.  If the mapping
            has been removed from the backing map (by the iterator's
            <tt>remove</tt> operation), the results of this call are undefined.

            @return the value corresponding to this entry.
            @aribaapi private
        */
        public Object getValue ()
        {
            return _value;
        }

        /**
            Replaces the value corresponding to this entry with the specified
            value (optional operation).  (Writes through to the map.)  The
            behavior of this call is undefined if the mapping has already been
            removed from the map (by the iterator's <tt>remove</tt> operation).

            @param value new value to be stored in this entry.
            @return old value corresponding to the entry.

            @throws UnsupportedOperationException if the <tt>put</tt> operation
                     is not supported by the backing map.
            @throws ClassCastException if the class of the specified value
                      prevents it from being stored in the backing map.
            @throws    IllegalArgumentException if some aspect of this value
                     prevents it from being stored in the backing map.
            @throws NullPointerException the backing map does not permit
                     <tt>null</tt> values, and the specified value is
                     <tt>null</tt>.
            @aribaapi private
        */
        public Object setValue (Object value)
        {
            Object oldValue = Hashtable.this.elements[_index];
            Hashtable.this.elements[_index]=value;
            return oldValue;
        }

        /**
            Compares the specified object with this entry for equality.
            Returns <tt>true</tt> if the given object is also a map entry and
            the two entries represent the same mapping.

            @param o object to be compared for equality with this map entry.
            @return <tt>true</tt> if the specified object is equal to this map
                    entry.
            @aribaapi private
        */
        public boolean equals (Object o)
        {
            if (! (o instanceof Map.Entry)) {
                return false;
            }
            Map.Entry e2 = (Map.Entry)o;

            return (getKey()==null ?
                e2.getKey()==null : getKey().equals(e2.getKey()))  &&
                (getValue()==null ?
                e2.getValue()==null : getValue().equals(e2.getValue()));
        }

        /**
            Returns the hash code value for this map entry.

            @return the hash code value for this map entry.
            @see Object#hashCode()
            @see Object#equals(Object)
            @see #equals(Object)
            @aribaapi private
        */
        public int hashCode ()
        {
            return _index;
        }
    }

    private class HashtableIterator implements Iterator
    {
        int index;
        int returnedCount;
        int type;
        boolean canRemove = false;

            // For efficiency, keep only one instead of creating
            // a new one for each iteration.  Iterators should be used only once, and
            // should not be shared.
        final HashtableEntry currentEntry = new HashtableEntry();

        public static final int Keys = 1;
        public static final int Values = 2;
        public static final int Entries = 3;

        HashtableIterator (int type)
        {
            this.type = type;
            returnedCount = 0;

            if (Hashtable.this.keys != null) {
                index = Hashtable.this.keys.length;
            }
            else {
                index = 0;
            }
        }

        /**
            Returns <tt>true</tt> if the iteration has more elements. (In other
            words, returns <tt>true</tt> if <tt>next</tt> would return an element
            rather than throwing an exception.)

            @return <tt>true</tt> if the iterator has more elements.
            @aribaapi private
        */
        public boolean hasNext ()
        {
            return (returnedCount < Hashtable.this.count);
        }

        /**
            Returns the next element in the interation.

            @return the next element in the iteration.
            @exception NoSuchElementException iteration has no more elements.
            @aribaapi private
        */
        public Object next ()
        {
            index--;

            while (index >= 0 &&
                   (Hashtable.this.elements[index] == null ||
                    Hashtable.this.elements[index] == Hashtable.DeletedMarker)) {
                index--;
            }

            if (index < 0 || returnedCount >= Hashtable.this.count) {
                throw new java.util.NoSuchElementException();
            }

            canRemove = true;
            returnedCount++;

            switch (type) {
                case Keys:
                    return Hashtable.this.keys[index];
                case Values:
                    return Hashtable.this.elements[index];
                case Entries:
                        // Avoid creating a new HashtableEntry for efficiency reasons.
                    currentEntry.setIndex(index);
                    return currentEntry;
                default:
                    Assert.that(false,"Invalid type");
                    return null;
            }
        }

        /**
            Removes from the underlying collection the last element returned by the
            iterator (optional operation).  This method can be called only once per
            call to <tt>next</tt>.  The behavior of an iterator is unspecified if
            the underlying collection is modified while the iteration is in
            progress in any way other than by calling this method.

            @exception UnsupportedOperationException if the <tt>remove</tt>
                     operation is not supported by this Iterator.

            @exception IllegalStateException if the <tt>next</tt> method has not
                     yet been called, or the <tt>remove</tt> method has already
                     been called after the last call to the <tt>next</tt>
                     method.

            @aribaapi private
        */
        public void remove ()
        {
            if (! canRemove) {
                throw new IllegalStateException("Removing before calling next()");
            }
            canRemove = false;
            Hashtable.this.removeAt(index);
            returnedCount--;
        }
    }

    private transient Set keySet = null;
    private transient Collection values = null;

    /**
        Returns a Set view of the keys contained in this map.  The Set is
        backed by the map, so changes to the map are reflected in the Set,
        and vice-versa.  (If the map is modified while an iteration over
        the Set is in progress, the results of the iteration are undefined.)
        The Set supports element removal, which removes the corresponding entry
        from the map, via the Iterator.remove, Set.remove,  removeAll
        retainAll, and clear operations.  It does not support the add or
        addAll operations.<p>

        @return a Set view of the keys contained in this map.
        @aribaapi documented
    */
    public Set keySet ()
    {
        if (keySet == null) {
            keySet = new AbstractSet()
            {
                public Iterator iterator ()
                {
                    return new HashtableIterator(HashtableIterator.Keys);
                }
                public int size ()
                {
                    return count;
                }
                public boolean contains (Object o)
                {
                    return containsKey(o);
                }
                public boolean remove (Object o)
                {
                    int oldSize = count;
                    Hashtable.this.remove(o);
                    return count != oldSize;
                }
                public void clear ()
                {
                    Hashtable.this.clear();
                }
            };
        }
        return keySet;
    }

    /**
        Returns a collection view of the values contained in this map.  The
        collection is backed by the map, so changes to the map are reflected in
        the collection, and vice-versa.  (If the map is modified while an
        iteration over the collection is in progress, the results of the
        iteration are undefined.)  The collection supports element removal,
        which removes the corresponding entry from the map, via the
        <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
        <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
        It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p>

        @return a collection view of the values contained in this map.
        @aribaapi documented
    */
    public Collection values ()
    {
        if (values == null) {
            values = new AbstractCollection ()
            {
                public Iterator iterator ()
                {
                    return new HashtableIterator(HashtableIterator.Values);
                }
                public int size ()
                {
                    return count;
                }
                public boolean contains (Object o)
                {
                    return containsValue(o);
                }
                public void clear ()
                {
                    Hashtable.this.clear();
                }
            };
        }
        return values;
    }

}
TOP

Related Classes of ariba.util.core.Hashtable$HashtableIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.