* @since 1.0
* @status updated to 1.4
*/
-public class Hashtable extends Dictionary
- implements Map, Cloneable, Serializable
+public class Hashtable<K, V> extends Dictionary<K, V>
+ implements Map<K, V>, Cloneable, Serializable
{
// WARNING: Hashtable is a CORE class in the bootstrap cycle. See the
// comments in vm/reference/java/lang/Runtime for implications of this fact.
* Array containing the actual key-value mappings.
*/
// Package visible for use by nested classes.
- transient HashEntry[] buckets;
+ transient HashEntry<K, V>[] buckets;
/**
* Counts the number of modifications this Hashtable has undergone, used
/**
* The cache for {@link #keySet()}.
*/
- private transient Set keys;
+ private transient Set<K> keys;
/**
* The cache for {@link #values()}.
*/
- private transient Collection values;
+ private transient Collection<V> values;
/**
* The cache for {@link #entrySet()}.
*/
- private transient Set entries;
+ private transient Set<Map.Entry<K, V>> entries;
/**
* Class to represent an entry in the hash table. Holds a single key-value
* pair. A Hashtable Entry is identical to a HashMap Entry, except that
* `null' is not allowed for keys and values.
*/
- private static final class HashEntry extends AbstractMap.BasicMapEntry
+ private static final class HashEntry<K, V>
+ extends AbstractMap.SimpleEntry<K, V>
{
/** The next entry in the linked list. */
- HashEntry next;
+ HashEntry<K, V> next;
/**
* Simple constructor.
* @param key the key, already guaranteed non-null
* @param value the value, already guaranteed non-null
*/
- HashEntry(Object key, Object value)
+ HashEntry(K key, V value)
{
super(key, value);
}
* @return the prior value
* @throws NullPointerException if <code>newVal</code> is null
*/
- public Object setValue(Object newVal)
+ public V setValue(V newVal)
{
if (newVal == null)
throw new NullPointerException();
* to or from `null'.
* @since 1.2
*/
- public Hashtable(Map m)
+ public Hashtable(Map<? extends K, ? extends V> m)
{
this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
putAll(m);
if (initialCapacity == 0)
initialCapacity = 1;
- buckets = new HashEntry[initialCapacity];
+ buckets = (HashEntry<K, V>[]) new HashEntry[initialCapacity];
this.loadFactor = loadFactor;
threshold = (int) (initialCapacity * loadFactor);
}
* @see #elements()
* @see #keySet()
*/
- public Enumeration keys()
+ public Enumeration<K> keys()
{
return new KeyEnumerator();
}
* @see #keys()
* @see #values()
*/
- public Enumeration elements()
+ public Enumeration<V> elements()
{
return new ValueEnumerator();
}
for (int i = buckets.length - 1; i >= 0; i--)
{
- HashEntry e = buckets[i];
+ HashEntry<K, V> e = buckets[i];
while (e != null)
{
if (e.value.equals(value))
e = e.next;
}
}
-
+
return false;
}
public synchronized boolean containsKey(Object key)
{
int idx = hash(key);
- HashEntry e = buckets[idx];
+ HashEntry<K, V> e = buckets[idx];
while (e != null)
{
if (e.key.equals(key))
* @see #put(Object, Object)
* @see #containsKey(Object)
*/
- public synchronized Object get(Object key)
+ public synchronized V get(Object key)
{
int idx = hash(key);
- HashEntry e = buckets[idx];
+ HashEntry<K, V> e = buckets[idx];
while (e != null)
{
if (e.key.equals(key))
* @see #get(Object)
* @see Object#equals(Object)
*/
- public synchronized Object put(Object key, Object value)
+ public synchronized V put(K key, V value)
{
int idx = hash(key);
- HashEntry e = buckets[idx];
+ HashEntry<K, V> e = buckets[idx];
// Check if value is null since it is not permitted.
if (value == null)
if (e.key.equals(key))
{
// Bypass e.setValue, since we already know value is non-null.
- Object r = e.value;
+ V r = e.value;
e.value = value;
return r;
}
idx = hash(key);
}
- e = new HashEntry(key, value);
+ e = new HashEntry<K, V>(key, value);
e.next = buckets[idx];
buckets[idx] = e;
* @param key the key used to locate the value to remove
* @return whatever the key mapped to, if present
*/
- public synchronized Object remove(Object key)
+ public synchronized V remove(Object key)
{
int idx = hash(key);
- HashEntry e = buckets[idx];
- HashEntry last = null;
+ HashEntry<K, V> e = buckets[idx];
+ HashEntry<K, V> last = null;
while (e != null)
{
* @param m the map to be hashed into this
* @throws NullPointerException if m is null, or contains null keys or values
*/
- public synchronized void putAll(Map m)
+ public synchronized void putAll(Map<? extends K, ? extends V> m)
{
- Iterator itr = m.entrySet().iterator();
+ Map<K,V> addMap;
+
+ addMap = (Map<K,V>) m;
- while (itr.hasNext())
+ for (Map.Entry<K,V> e : addMap.entrySet())
{
- Map.Entry e = (Map.Entry) itr.next();
// Optimize in case the Entry is one of our own.
- if (e instanceof AbstractMap.BasicMapEntry)
+ if (e instanceof AbstractMap.SimpleEntry)
{
- AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e;
+ AbstractMap.SimpleEntry<? extends K, ? extends V> entry
+ = (AbstractMap.SimpleEntry<? extends K, ? extends V>) e;
put(entry.key, entry.value);
}
else
*/
public synchronized Object clone()
{
- Hashtable copy = null;
+ Hashtable<K, V> copy = null;
try
{
- copy = (Hashtable) super.clone();
+ copy = (Hashtable<K, V>) super.clone();
}
catch (CloneNotSupportedException x)
{
// This is impossible.
}
- copy.buckets = new HashEntry[buckets.length];
+ copy.buckets = (HashEntry<K, V>[]) new HashEntry[buckets.length];
copy.putAllInternal(this);
// Clear the caches.
copy.keys = null;
// Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized EntryIterator instead.
- Iterator entries = new EntryIterator();
+ Iterator<Map.Entry<K, V>> entries = new EntryIterator();
StringBuffer r = new StringBuffer("{");
for (int pos = size; pos > 0; pos--)
{
* @see #entrySet()
* @since 1.2
*/
- public Set keySet()
+ public Set<K> keySet()
{
if (keys == null)
{
// Create a synchronized AbstractSet with custom implementations of
// those methods that can be overridden easily and efficiently.
- Set r = new AbstractSet()
+ Set<K> r = new AbstractSet<K>()
{
public int size()
{
return size;
}
- public Iterator iterator()
+ public Iterator<K> iterator()
{
return new KeyIterator();
}
};
// We must specify the correct object to synchronize upon, hence the
// use of a non-public API
- keys = new Collections.SynchronizedSet(this, r);
+ keys = new Collections.SynchronizedSet<K>(this, r);
}
return keys;
}
* @see #entrySet()
* @since 1.2
*/
- public Collection values()
+ public Collection<V> values()
{
if (values == null)
{
// We don't bother overriding many of the optional methods, as doing so
// wouldn't provide any significant performance advantage.
- Collection r = new AbstractCollection()
+ Collection<V> r = new AbstractCollection<V>()
{
public int size()
{
return size;
}
- public Iterator iterator()
+ public Iterator<V> iterator()
{
return new ValueIterator();
}
};
// We must specify the correct object to synchronize upon, hence the
// use of a non-public API
- values = new Collections.SynchronizedCollection(this, r);
+ values = new Collections.SynchronizedCollection<V>(this, r);
}
return values;
}
* @see Map.Entry
* @since 1.2
*/
- public Set entrySet()
+ public Set<Map.Entry<K, V>> entrySet()
{
if (entries == null)
{
// Create an AbstractSet with custom implementations of those methods
// that can be overridden easily and efficiently.
- Set r = new AbstractSet()
+ Set<Map.Entry<K, V>> r = new AbstractSet<Map.Entry<K, V>>()
{
public int size()
{
return size;
}
- public Iterator iterator()
+ public Iterator<Map.Entry<K, V>> iterator()
{
return new EntryIterator();
}
public boolean remove(Object o)
{
- HashEntry e = getEntry(o);
+ HashEntry<K, V> e = getEntry(o);
if (e != null)
{
Hashtable.this.remove(e.key);
};
// We must specify the correct object to synchronize upon, hence the
// use of a non-public API
- entries = new Collections.SynchronizedSet(this, r);
+ entries = new Collections.SynchronizedSet<Map.Entry<K, V>>(this, r);
}
return entries;
}
*/
public boolean equals(Object o)
{
- // no need to synchronize, entrySet().equals() does that
+ // no need to synchronize, entrySet().equals() does that.
if (o == this)
return true;
if (!(o instanceof Map))
// Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized EntryIterator instead.
- Iterator itr = new EntryIterator();
+ Iterator<Map.Entry<K, V>> itr = new EntryIterator();
int hashcode = 0;
for (int pos = size; pos > 0; pos--)
hashcode += itr.next().hashCode();
* @see #entrySet()
*/
// Package visible, for use in nested classes.
- HashEntry getEntry(Object o)
+ HashEntry<K, V> getEntry(Object o)
{
if (! (o instanceof Map.Entry))
return null;
- Object key = ((Map.Entry) o).getKey();
+ K key = ((Map.Entry<K, V>) o).getKey();
if (key == null)
return null;
int idx = hash(key);
- HashEntry e = buckets[idx];
+ HashEntry<K, V> e = buckets[idx];
while (e != null)
{
if (e.equals(o))
*
* @param m the map to initialize this from
*/
- void putAllInternal(Map m)
+ void putAllInternal(Map<? extends K, ? extends V> m)
{
- Iterator itr = m.entrySet().iterator();
+ Map<K,V> addMap;
+
+ addMap = (Map<K,V>) m;
size = 0;
- while (itr.hasNext())
+ for (Map.Entry<K,V> e : addMap.entrySet())
{
size++;
- Map.Entry e = (Map.Entry) itr.next();
- Object key = e.getKey();
+ K key = e.getKey();
int idx = hash(key);
- HashEntry he = new HashEntry(key, e.getValue());
+ HashEntry<K, V> he = new HashEntry<K, V>(key, e.getValue());
he.next = buckets[idx];
buckets[idx] = he;
}
*/
protected void rehash()
{
- HashEntry[] oldBuckets = buckets;
+ HashEntry<K, V>[] oldBuckets = buckets;
int newcapacity = (buckets.length * 2) + 1;
threshold = (int) (newcapacity * loadFactor);
- buckets = new HashEntry[newcapacity];
+ buckets = (HashEntry<K, V>[]) new HashEntry[newcapacity];
for (int i = oldBuckets.length - 1; i >= 0; i--)
{
- HashEntry e = oldBuckets[i];
+ HashEntry<K, V> e = oldBuckets[i];
while (e != null)
{
int idx = hash(e.key);
- HashEntry dest = buckets[idx];
+ HashEntry<K, V> dest = buckets[idx];
if (dest != null)
{
buckets[idx] = e;
}
- HashEntry next = e.next;
+ HashEntry<K, V> next = e.next;
e.next = null;
e = next;
}
// Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized EntryIterator instead.
- Iterator it = new EntryIterator();
+ Iterator<Map.Entry<K, V>> it = new EntryIterator();
while (it.hasNext())
{
- HashEntry entry = (HashEntry) it.next();
+ HashEntry<K, V> entry = (HashEntry<K, V>) it.next();
s.writeObject(entry.key);
s.writeObject(entry.value);
}
s.defaultReadObject();
// Read and use capacity.
- buckets = new HashEntry[s.readInt()];
+ buckets = (HashEntry<K, V>[]) new HashEntry[s.readInt()];
int len = s.readInt();
// Read and use key/value pairs.
// TODO: should we be defensive programmers, and check for illegal nulls?
while (--len >= 0)
- put(s.readObject(), s.readObject());
+ put((K) s.readObject(), (V) s.readObject());
}
/**
* @author Jon Zeppieri
* @author Fridjof Siebert
*/
- private class EntryIterator implements Iterator
+ private class EntryIterator
+ implements Iterator<Entry<K,V>>
{
/**
* The number of modifications to the backing Hashtable that we know about.
/** Current index in the physical hash table. */
int idx = buckets.length;
/** The last Entry returned by a next() call. */
- HashEntry last;
+ HashEntry<K, V> last;
/**
* The next entry that should be returned by next(). It is set to something
* if we're iterating through a bucket that contains multiple linked
* entries. It is null if next() needs to find a new bucket.
*/
- HashEntry next;
+ HashEntry<K, V> next;
/**
- * Construct a new EtryIterator
+ * Construct a new EntryIterator
*/
EntryIterator()
{
* @throws ConcurrentModificationException if the hashtable was modified
* @throws NoSuchElementException if there is none
*/
- public Object next()
+ public Map.Entry<K,V> next()
{
if (knownMod != modCount)
throw new ConcurrentModificationException();
if (count == 0)
throw new NoSuchElementException();
count--;
- HashEntry e = next;
+ HashEntry<K, V> e = next;
while (e == null)
if (idx <= 0)
/**
* A class which implements the Iterator interface and is used for
- * iterating over keys in Hashtables.
+ * iterating over keys in Hashtables. This class uses an
+ * <code>EntryIterator</code> to obtain the keys of each entry.
*
* @author Fridtjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
- private class KeyIterator extends EntryIterator
+ private class KeyIterator
+ implements Iterator<K>
{
+
+ /**
+ * This entry iterator is used for most operations. Only
+ * <code>next()</code> gives a different result, by returning just
+ * the key rather than the whole element.
+ */
+ private EntryIterator iterator;
+
+ /**
+ * Construct a new KeyIterator
+ */
+ KeyIterator()
+ {
+ iterator = new EntryIterator();
+ }
+
+
+ /**
+ * Returns true if the entry iterator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
/**
* Returns the next element in the Iterator's sequential view.
*
* @throws ConcurrentModificationException if the hashtable was modified
* @throws NoSuchElementException if there is none
*/
- public Object next()
+ public K next()
{
- return ((HashEntry)super.next()).key;
+ return ((HashEntry<K,V>) iterator.next()).key;
}
- } // class KeyIterator
-
-
+ /**
+ * Removes the last element used by the <code>next()</code> method
+ * using the entry iterator.
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ iterator.remove();
+ }
+ } // class KeyIterator
+
/**
* A class which implements the Iterator interface and is used for
- * iterating over values in Hashtables.
+ * iterating over values in Hashtables. This class uses an
+ * <code>EntryIterator</code> to obtain the values of each entry.
*
* @author Fridtjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
- private class ValueIterator extends EntryIterator
+ private class ValueIterator
+ implements Iterator<V>
{
+
/**
- * Returns the next element in the Iterator's sequential view.
+ * This entry iterator is used for most operations. Only
+ * <code>next()</code> gives a different result, by returning just
+ * the value rather than the whole element.
+ */
+ private EntryIterator iterator;
+
+ /**
+ * Construct a new KeyIterator
+ */
+ ValueIterator()
+ {
+ iterator = new EntryIterator();
+ }
+
+
+ /**
+ * Returns true if the entry iterator has more elements.
*
- * @return the next element
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ /**
+ * Returns the value of the next element in the iterator's sequential view.
+ *
+ * @return the next value
*
* @throws ConcurrentModificationException if the hashtable was modified
* @throws NoSuchElementException if there is none
*/
- public Object next()
+ public V next()
+ {
+ return ((HashEntry<K,V>) iterator.next()).value;
+ }
+
+ /**
+ * Removes the last element used by the <code>next()</code> method
+ * using the entry iterator.
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
{
- return ((HashEntry)super.next()).value;
+ iterator.remove();
}
+
} // class ValueIterator
/**
* @author Jon Zeppieri
* @author Fridjof Siebert
*/
- private class EntryEnumerator implements Enumeration
+ private class EntryEnumerator
+ implements Enumeration<Entry<K,V>>
{
/** The number of elements remaining to be returned by next(). */
int count = size;
* set if we are iterating through a bucket with multiple entries, or null
* if we must look in the next bucket.
*/
- HashEntry next;
+ HashEntry<K, V> next;
/**
* Construct the enumeration.
* @return the next element
* @throws NoSuchElementException if there is none.
*/
- public Object nextElement()
+ public Map.Entry<K,V> nextElement()
{
if (count == 0)
throw new NoSuchElementException("Hashtable Enumerator");
count--;
- HashEntry e = next;
+ HashEntry<K, V> e = next;
while (e == null)
if (idx <= 0)
*
* @author Jon Zeppieri
* @author Fridjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
- private final class KeyEnumerator extends EntryEnumerator
+ private final class KeyEnumerator
+ implements Enumeration<K>
{
/**
+ * This entry enumerator is used for most operations. Only
+ * <code>nextElement()</code> gives a different result, by returning just
+ * the key rather than the whole element.
+ */
+ private EntryEnumerator enumerator;
+
+ /**
+ * Construct a new KeyEnumerator
+ */
+ KeyEnumerator()
+ {
+ enumerator = new EntryEnumerator();
+ }
+
+
+ /**
+ * Returns true if the entry enumerator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasMoreElements()
+ {
+ return enumerator.hasMoreElements();
+ }
+
+ /**
* Returns the next element.
* @return the next element
* @throws NoSuchElementException if there is none.
*/
- public Object nextElement()
+ public K nextElement()
{
- HashEntry entry = (HashEntry) super.nextElement();
- Object retVal = null;
+ HashEntry<K,V> entry = (HashEntry<K,V>) enumerator.nextElement();
+ K retVal = null;
if (entry != null)
retVal = entry.key;
return retVal;
*
* @author Jon Zeppieri
* @author Fridjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
- private final class ValueEnumerator extends EntryEnumerator
+ private final class ValueEnumerator
+ implements Enumeration<V>
{
/**
+ * This entry enumerator is used for most operations. Only
+ * <code>nextElement()</code> gives a different result, by returning just
+ * the value rather than the whole element.
+ */
+ private EntryEnumerator enumerator;
+
+ /**
+ * Construct a new ValueEnumerator
+ */
+ ValueEnumerator()
+ {
+ enumerator = new EntryEnumerator();
+ }
+
+
+ /**
+ * Returns true if the entry enumerator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasMoreElements()
+ {
+ return enumerator.hasMoreElements();
+ }
+
+ /**
* Returns the next element.
* @return the next element
* @throws NoSuchElementException if there is none.
*/
- public Object nextElement()
+ public V nextElement()
{
- HashEntry entry = (HashEntry) super.nextElement();
- Object retVal = null;
+ HashEntry<K,V> entry = (HashEntry<K,V>) enumerator.nextElement();
+ V retVal = null;
if (entry != null)
retVal = entry.value;
return retVal;