99 |
* @since 1.0 |
* @since 1.0 |
100 |
* @status updated to 1.4 |
* @status updated to 1.4 |
101 |
*/ |
*/ |
102 |
public class Hashtable extends Dictionary |
public class Hashtable<K, V> extends Dictionary<K, V> |
103 |
implements Map, Cloneable, Serializable |
implements Map<K, V>, Cloneable, Serializable |
104 |
{ |
{ |
105 |
// WARNING: Hashtable is a CORE class in the bootstrap cycle. See the |
// WARNING: Hashtable is a CORE class in the bootstrap cycle. See the |
106 |
// comments in vm/reference/java/lang/Runtime for implications of this fact. |
// comments in vm/reference/java/lang/Runtime for implications of this fact. |
144 |
* Array containing the actual key-value mappings. |
* Array containing the actual key-value mappings. |
145 |
*/ |
*/ |
146 |
// Package visible for use by nested classes. |
// Package visible for use by nested classes. |
147 |
transient HashEntry[] buckets; |
transient HashEntry<K, V>[] buckets; |
148 |
|
|
149 |
/** |
/** |
150 |
* Counts the number of modifications this Hashtable has undergone, used |
* Counts the number of modifications this Hashtable has undergone, used |
162 |
/** |
/** |
163 |
* The cache for {@link #keySet()}. |
* The cache for {@link #keySet()}. |
164 |
*/ |
*/ |
165 |
private transient Set keys; |
private transient Set<K> keys; |
166 |
|
|
167 |
/** |
/** |
168 |
* The cache for {@link #values()}. |
* The cache for {@link #values()}. |
169 |
*/ |
*/ |
170 |
private transient Collection values; |
private transient Collection<V> values; |
171 |
|
|
172 |
/** |
/** |
173 |
* The cache for {@link #entrySet()}. |
* The cache for {@link #entrySet()}. |
174 |
*/ |
*/ |
175 |
private transient Set entries; |
private transient Set<HashEntry<K, V>> entries; |
176 |
|
|
177 |
/** |
/** |
178 |
* Class to represent an entry in the hash table. Holds a single key-value |
* Class to represent an entry in the hash table. Holds a single key-value |
179 |
* pair. A Hashtable Entry is identical to a HashMap Entry, except that |
* pair. A Hashtable Entry is identical to a HashMap Entry, except that |
180 |
* `null' is not allowed for keys and values. |
* `null' is not allowed for keys and values. |
181 |
*/ |
*/ |
182 |
private static final class HashEntry extends AbstractMap.BasicMapEntry |
private static final class HashEntry<K, V> |
183 |
|
extends AbstractMap.BasicMapEntry<K, V> |
184 |
{ |
{ |
185 |
/** The next entry in the linked list. */ |
/** The next entry in the linked list. */ |
186 |
HashEntry next; |
HashEntry<K, V> next; |
187 |
|
|
188 |
/** |
/** |
189 |
* Simple constructor. |
* Simple constructor. |
190 |
* @param key the key, already guaranteed non-null |
* @param key the key, already guaranteed non-null |
191 |
* @param value the value, already guaranteed non-null |
* @param value the value, already guaranteed non-null |
192 |
*/ |
*/ |
193 |
HashEntry(Object key, Object value) |
HashEntry(K key, V value) |
194 |
{ |
{ |
195 |
super(key, value); |
super(key, value); |
196 |
} |
} |
201 |
* @return the prior value |
* @return the prior value |
202 |
* @throws NullPointerException if <code>newVal</code> is null |
* @throws NullPointerException if <code>newVal</code> is null |
203 |
*/ |
*/ |
204 |
public Object setValue(Object newVal) |
public V setValue(V newVal) |
205 |
{ |
{ |
206 |
if (newVal == null) |
if (newVal == null) |
207 |
throw new NullPointerException(); |
throw new NullPointerException(); |
232 |
* to or from `null'. |
* to or from `null'. |
233 |
* @since 1.2 |
* @since 1.2 |
234 |
*/ |
*/ |
235 |
public Hashtable(Map m) |
public Hashtable(Map<? extends K, ? extends V> m) |
236 |
{ |
{ |
237 |
this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); |
this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); |
238 |
putAll(m); |
putAll(m); |
269 |
|
|
270 |
if (initialCapacity == 0) |
if (initialCapacity == 0) |
271 |
initialCapacity = 1; |
initialCapacity = 1; |
272 |
buckets = new HashEntry[initialCapacity]; |
buckets = new HashEntry<K, V>[initialCapacity]; |
273 |
this.loadFactor = loadFactor; |
this.loadFactor = loadFactor; |
274 |
threshold = (int) (initialCapacity * loadFactor); |
threshold = (int) (initialCapacity * loadFactor); |
275 |
} |
} |
301 |
* @see #elements() |
* @see #elements() |
302 |
* @see #keySet() |
* @see #keySet() |
303 |
*/ |
*/ |
304 |
public Enumeration keys() |
public Enumeration<K> keys() |
305 |
{ |
{ |
306 |
return new Enumerator(KEYS); |
return new Enumerator<K>(KEYS); |
307 |
} |
} |
308 |
|
|
309 |
/** |
/** |
317 |
*/ |
*/ |
318 |
public Enumeration elements() |
public Enumeration elements() |
319 |
{ |
{ |
320 |
return new Enumerator(VALUES); |
return new Enumerator<V>(VALUES); |
321 |
} |
} |
322 |
|
|
323 |
/** |
/** |
336 |
{ |
{ |
337 |
for (int i = buckets.length - 1; i >= 0; i--) |
for (int i = buckets.length - 1; i >= 0; i--) |
338 |
{ |
{ |
339 |
HashEntry e = buckets[i]; |
HashEntry<K, V> e = buckets[i]; |
340 |
while (e != null) |
while (e != null) |
341 |
{ |
{ |
342 |
if (value.equals(e.value)) |
if (value.equals(e.value)) |
348 |
// Must throw on null argument even if the table is empty |
// Must throw on null argument even if the table is empty |
349 |
if (value == null) |
if (value == null) |
350 |
throw new NullPointerException(); |
throw new NullPointerException(); |
351 |
|
|
352 |
return false; |
return false; |
353 |
} |
} |
354 |
|
|
383 |
public synchronized boolean containsKey(Object key) |
public synchronized boolean containsKey(Object key) |
384 |
{ |
{ |
385 |
int idx = hash(key); |
int idx = hash(key); |
386 |
HashEntry e = buckets[idx]; |
HashEntry<K, V> e = buckets[idx]; |
387 |
while (e != null) |
while (e != null) |
388 |
{ |
{ |
389 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
403 |
* @see #put(Object, Object) |
* @see #put(Object, Object) |
404 |
* @see #containsKey(Object) |
* @see #containsKey(Object) |
405 |
*/ |
*/ |
406 |
public synchronized Object get(Object key) |
public synchronized V get(Object key) |
407 |
{ |
{ |
408 |
int idx = hash(key); |
int idx = hash(key); |
409 |
HashEntry e = buckets[idx]; |
HashEntry<K, V> e = buckets[idx]; |
410 |
while (e != null) |
while (e != null) |
411 |
{ |
{ |
412 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
428 |
* @see #get(Object) |
* @see #get(Object) |
429 |
* @see Object#equals(Object) |
* @see Object#equals(Object) |
430 |
*/ |
*/ |
431 |
public synchronized Object put(Object key, Object value) |
public synchronized V put(K key, V value) |
432 |
{ |
{ |
433 |
int idx = hash(key); |
int idx = hash(key); |
434 |
HashEntry e = buckets[idx]; |
HashEntry<K, V> e = buckets[idx]; |
435 |
|
|
436 |
// Check if value is null since it is not permitted. |
// Check if value is null since it is not permitted. |
437 |
if (value == null) |
if (value == null) |
442 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
443 |
{ |
{ |
444 |
// Bypass e.setValue, since we already know value is non-null. |
// Bypass e.setValue, since we already know value is non-null. |
445 |
Object r = e.value; |
V r = e.value; |
446 |
e.value = value; |
e.value = value; |
447 |
return r; |
return r; |
448 |
} |
} |
461 |
idx = hash(key); |
idx = hash(key); |
462 |
} |
} |
463 |
|
|
464 |
e = new HashEntry(key, value); |
e = new HashEntry<K, V>(key, value); |
465 |
|
|
466 |
e.next = buckets[idx]; |
e.next = buckets[idx]; |
467 |
buckets[idx] = e; |
buckets[idx] = e; |
477 |
* @param key the key used to locate the value to remove |
* @param key the key used to locate the value to remove |
478 |
* @return whatever the key mapped to, if present |
* @return whatever the key mapped to, if present |
479 |
*/ |
*/ |
480 |
public synchronized Object remove(Object key) |
public synchronized V remove(Object key) |
481 |
{ |
{ |
482 |
int idx = hash(key); |
int idx = hash(key); |
483 |
HashEntry e = buckets[idx]; |
HashEntry<K, V> e = buckets[idx]; |
484 |
HashEntry last = null; |
HashEntry<K, V> last = null; |
485 |
|
|
486 |
while (e != null) |
while (e != null) |
487 |
{ |
{ |
509 |
* @param m the map to be hashed into this |
* @param m the map to be hashed into this |
510 |
* @throws NullPointerException if m is null, or contains null keys or values |
* @throws NullPointerException if m is null, or contains null keys or values |
511 |
*/ |
*/ |
512 |
public synchronized void putAll(Map m) |
public synchronized void putAll(Map<? extends K, ? extends V> m) |
513 |
{ |
{ |
514 |
Iterator itr = m.entrySet().iterator(); |
Iterator<Map.Entry<? extends K, ? extends V>> itr |
515 |
|
= m.entrySet().iterator(); |
516 |
|
|
517 |
while (itr.hasNext()) |
while (itr.hasNext()) |
518 |
{ |
{ |
519 |
Map.Entry e = (Map.Entry) itr.next(); |
Map.Entry<? extends K, ? extends V> e |
520 |
|
= (Map.Entry<? extends K, ? extends V>) itr.next(); |
521 |
// Optimize in case the Entry is one of our own. |
// Optimize in case the Entry is one of our own. |
522 |
if (e instanceof AbstractMap.BasicMapEntry) |
if (e instanceof AbstractMap.BasicMapEntry) |
523 |
{ |
{ |
524 |
AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e; |
AbstractMap.BasicMapEntry<? extends K, ? extends V> entry |
525 |
|
= (AbstractMap.BasicMapEntry<? extends K, ? extends V>) e; |
526 |
put(entry.key, entry.value); |
put(entry.key, entry.value); |
527 |
} |
} |
528 |
else |
else |
553 |
*/ |
*/ |
554 |
public synchronized Object clone() |
public synchronized Object clone() |
555 |
{ |
{ |
556 |
Hashtable copy = null; |
Hashtable<K, V> copy = null; |
557 |
try |
try |
558 |
{ |
{ |
559 |
copy = (Hashtable) super.clone(); |
copy = (Hashtable<K, V>) super.clone(); |
560 |
} |
} |
561 |
catch (CloneNotSupportedException x) |
catch (CloneNotSupportedException x) |
562 |
{ |
{ |
563 |
// This is impossible. |
// This is impossible. |
564 |
} |
} |
565 |
copy.buckets = new HashEntry[buckets.length]; |
copy.buckets = new HashEntry<K, V>[buckets.length]; |
566 |
copy.putAllInternal(this); |
copy.putAllInternal(this); |
567 |
// Clear the caches. |
// Clear the caches. |
568 |
copy.keys = null; |
copy.keys = null; |
586 |
// Since we are already synchronized, and entrySet().iterator() |
// Since we are already synchronized, and entrySet().iterator() |
587 |
// would repeatedly re-lock/release the monitor, we directly use the |
// would repeatedly re-lock/release the monitor, we directly use the |
588 |
// unsynchronized HashIterator instead. |
// unsynchronized HashIterator instead. |
589 |
Iterator entries = new HashIterator(ENTRIES); |
Iterator<Map.Entry<? extends K, ? extends V>> entries |
590 |
|
= new HashIterator<Map.Entry<? extends K, ? extends V>>(ENTRIES); |
591 |
StringBuffer r = new StringBuffer("{"); |
StringBuffer r = new StringBuffer("{"); |
592 |
for (int pos = size; pos > 0; pos--) |
for (int pos = size; pos > 0; pos--) |
593 |
{ |
{ |
614 |
* @see #entrySet() |
* @see #entrySet() |
615 |
* @since 1.2 |
* @since 1.2 |
616 |
*/ |
*/ |
617 |
public Set keySet() |
public Set<K> keySet() |
618 |
{ |
{ |
619 |
if (keys == null) |
if (keys == null) |
620 |
{ |
{ |
621 |
// Create a synchronized AbstractSet with custom implementations of |
// Create a synchronized AbstractSet with custom implementations of |
622 |
// those methods that can be overridden easily and efficiently. |
// those methods that can be overridden easily and efficiently. |
623 |
Set r = new AbstractSet() |
Set<K> r = new AbstractSet<K>() |
624 |
{ |
{ |
625 |
public int size() |
public int size() |
626 |
{ |
{ |
627 |
return size; |
return size; |
628 |
} |
} |
629 |
|
|
630 |
public Iterator iterator() |
public Iterator<K> iterator() |
631 |
{ |
{ |
632 |
return new HashIterator(KEYS); |
return new HashIterator<K>(KEYS); |
633 |
} |
} |
634 |
|
|
635 |
public void clear() |
public void clear() |
651 |
}; |
}; |
652 |
// We must specify the correct object to synchronize upon, hence the |
// We must specify the correct object to synchronize upon, hence the |
653 |
// use of a non-public API |
// use of a non-public API |
654 |
keys = new Collections.SynchronizedSet(this, r); |
keys = new Collections.SynchronizedSet<K>(this, r); |
655 |
} |
} |
656 |
return keys; |
return keys; |
657 |
} |
} |
672 |
* @see #entrySet() |
* @see #entrySet() |
673 |
* @since 1.2 |
* @since 1.2 |
674 |
*/ |
*/ |
675 |
public Collection values() |
public Collection<V> values() |
676 |
{ |
{ |
677 |
if (values == null) |
if (values == null) |
678 |
{ |
{ |
679 |
// We don't bother overriding many of the optional methods, as doing so |
// We don't bother overriding many of the optional methods, as doing so |
680 |
// wouldn't provide any significant performance advantage. |
// wouldn't provide any significant performance advantage. |
681 |
Collection r = new AbstractCollection() |
Collection<V> r = new AbstractCollection<V>() |
682 |
{ |
{ |
683 |
public int size() |
public int size() |
684 |
{ |
{ |
685 |
return size; |
return size; |
686 |
} |
} |
687 |
|
|
688 |
public Iterator iterator() |
public Iterator<V> iterator() |
689 |
{ |
{ |
690 |
return new HashIterator(VALUES); |
return new HashIterator<V>(VALUES); |
691 |
} |
} |
692 |
|
|
693 |
public void clear() |
public void clear() |
697 |
}; |
}; |
698 |
// We must specify the correct object to synchronize upon, hence the |
// We must specify the correct object to synchronize upon, hence the |
699 |
// use of a non-public API |
// use of a non-public API |
700 |
values = new Collections.SynchronizedCollection(this, r); |
values = new Collections.SynchronizedCollection<V>(this, r); |
701 |
} |
} |
702 |
return values; |
return values; |
703 |
} |
} |
724 |
* @see Map.Entry |
* @see Map.Entry |
725 |
* @since 1.2 |
* @since 1.2 |
726 |
*/ |
*/ |
727 |
public Set entrySet() |
public Set<Map.Entry<K, V>> entrySet() |
728 |
{ |
{ |
729 |
if (entries == null) |
if (entries == null) |
730 |
{ |
{ |
731 |
// Create an AbstractSet with custom implementations of those methods |
// Create an AbstractSet with custom implementations of those methods |
732 |
// that can be overridden easily and efficiently. |
// that can be overridden easily and efficiently. |
733 |
Set r = new AbstractSet() |
Set<Map.Entry<K, V>> r = new AbstractSet<Map.Entry<K, V>>() |
734 |
{ |
{ |
735 |
public int size() |
public int size() |
736 |
{ |
{ |
737 |
return size; |
return size; |
738 |
} |
} |
739 |
|
|
740 |
public Iterator iterator() |
public Iterator<Map.Entry<K, V>> iterator() |
741 |
{ |
{ |
742 |
return new HashIterator(ENTRIES); |
return new HashIterator<Map.Entry<K, V>>(ENTRIES); |
743 |
} |
} |
744 |
|
|
745 |
public void clear() |
public void clear() |
754 |
|
|
755 |
public boolean remove(Object o) |
public boolean remove(Object o) |
756 |
{ |
{ |
757 |
HashEntry e = getEntry(o); |
HashEntry<K, V> e = getEntry(o); |
758 |
if (e != null) |
if (e != null) |
759 |
{ |
{ |
760 |
Hashtable.this.remove(e.key); |
Hashtable.this.remove(e.key); |
765 |
}; |
}; |
766 |
// We must specify the correct object to synchronize upon, hence the |
// We must specify the correct object to synchronize upon, hence the |
767 |
// use of a non-public API |
// use of a non-public API |
768 |
entries = new Collections.SynchronizedSet(this, r); |
entries = new Collections.SynchronizedSet<Map.Entry<K, V>>(this, r); |
769 |
} |
} |
770 |
return entries; |
return entries; |
771 |
} |
} |
783 |
*/ |
*/ |
784 |
public boolean equals(Object o) |
public boolean equals(Object o) |
785 |
{ |
{ |
786 |
// no need to synchronize, entrySet().equals() does that |
// No need to synchronize, entrySet().equals() does that. |
787 |
if (o == this) |
if (o == this) |
788 |
return true; |
return true; |
789 |
if (!(o instanceof Map)) |
if (!(o instanceof Map)) |
804 |
// Since we are already synchronized, and entrySet().iterator() |
// Since we are already synchronized, and entrySet().iterator() |
805 |
// would repeatedly re-lock/release the monitor, we directly use the |
// would repeatedly re-lock/release the monitor, we directly use the |
806 |
// unsynchronized HashIterator instead. |
// unsynchronized HashIterator instead. |
807 |
Iterator itr = new HashIterator(ENTRIES); |
Iterator<Map.Entry<K, V>> itr = new HashIterator<Map.Entry<K, V>>(ENTRIES); |
808 |
int hashcode = 0; |
int hashcode = 0; |
809 |
for (int pos = size; pos > 0; pos--) |
for (int pos = size; pos > 0; pos--) |
810 |
hashcode += itr.next().hashCode(); |
hashcode += itr.next().hashCode(); |
820 |
* @return the bucket number |
* @return the bucket number |
821 |
* @throws NullPointerException if key is null |
* @throws NullPointerException if key is null |
822 |
*/ |
*/ |
823 |
private int hash(Object key) |
private int hash(K key) |
824 |
{ |
{ |
825 |
// Note: Inline Math.abs here, for less method overhead, and to avoid |
// Note: Inline Math.abs here, for less method overhead, and to avoid |
826 |
// a bootstrap dependency, since Math relies on native methods. |
// a bootstrap dependency, since Math relies on native methods. |
837 |
* @see #entrySet() |
* @see #entrySet() |
838 |
*/ |
*/ |
839 |
// Package visible, for use in nested classes. |
// Package visible, for use in nested classes. |
840 |
HashEntry getEntry(Object o) |
HashEntry<K, V> getEntry(Object o) |
841 |
{ |
{ |
842 |
if (! (o instanceof Map.Entry)) |
if (! (o instanceof Map.Entry<K, V>)) |
843 |
return null; |
return null; |
844 |
Object key = ((Map.Entry) o).getKey(); |
K key = ((Map.Entry<K, V>) o).getKey(); |
845 |
if (key == null) |
if (key == null) |
846 |
return null; |
return null; |
847 |
|
|
848 |
int idx = hash(key); |
int idx = hash(key); |
849 |
HashEntry e = buckets[idx]; |
HashEntry<K, V> e = buckets[idx]; |
850 |
while (e != null) |
while (e != null) |
851 |
{ |
{ |
852 |
if (o.equals(e)) |
if (o.equals(e)) |
863 |
* |
* |
864 |
* @param m the map to initialize this from |
* @param m the map to initialize this from |
865 |
*/ |
*/ |
866 |
void putAllInternal(Map m) |
void putAllInternal(Map<? extends K, ? extends V> m) |
867 |
{ |
{ |
868 |
Iterator itr = m.entrySet().iterator(); |
Iterator<Map.Entry<? extends K, ? extends V>> itr |
869 |
|
= m.entrySet().iterator(); |
870 |
size = 0; |
size = 0; |
871 |
|
|
872 |
while (itr.hasNext()) |
while (itr.hasNext()) |
873 |
{ |
{ |
874 |
size++; |
size++; |
875 |
Map.Entry e = (Map.Entry) itr.next(); |
Map.Entry<? extends K, ? extends V> e |
876 |
Object key = e.getKey(); |
= (Map.Entry<? extends K, ? extends V>) itr.next(); |
877 |
|
K key = e.getKey(); |
878 |
int idx = hash(key); |
int idx = hash(key); |
879 |
HashEntry he = new HashEntry(key, e.getValue()); |
HashEntry<K, V> he = new HashEntry<K, V>(key, e.getValue()); |
880 |
he.next = buckets[idx]; |
he.next = buckets[idx]; |
881 |
buckets[idx] = he; |
buckets[idx] = he; |
882 |
} |
} |
895 |
*/ |
*/ |
896 |
protected void rehash() |
protected void rehash() |
897 |
{ |
{ |
898 |
HashEntry[] oldBuckets = buckets; |
HashEntry<K, V>[] oldBuckets = buckets; |
899 |
|
|
900 |
int newcapacity = (buckets.length * 2) + 1; |
int newcapacity = (buckets.length * 2) + 1; |
901 |
threshold = (int) (newcapacity * loadFactor); |
threshold = (int) (newcapacity * loadFactor); |
902 |
buckets = new HashEntry[newcapacity]; |
buckets = new HashEntry<K, V>[newcapacity]; |
903 |
|
|
904 |
for (int i = oldBuckets.length - 1; i >= 0; i--) |
for (int i = oldBuckets.length - 1; i >= 0; i--) |
905 |
{ |
{ |
906 |
HashEntry e = oldBuckets[i]; |
HashEntry<K, V> e = oldBuckets[i]; |
907 |
while (e != null) |
while (e != null) |
908 |
{ |
{ |
909 |
int idx = hash(e.key); |
int idx = hash(e.key); |
910 |
HashEntry dest = buckets[idx]; |
HashEntry<K, V> dest = buckets[idx]; |
911 |
|
|
912 |
if (dest != null) |
if (dest != null) |
913 |
{ |
{ |
920 |
buckets[idx] = e; |
buckets[idx] = e; |
921 |
} |
} |
922 |
|
|
923 |
HashEntry next = e.next; |
HashEntry<K, V> next = e.next; |
924 |
e.next = null; |
e.next = null; |
925 |
e = next; |
e = next; |
926 |
} |
} |
948 |
// Since we are already synchronized, and entrySet().iterator() |
// Since we are already synchronized, and entrySet().iterator() |
949 |
// would repeatedly re-lock/release the monitor, we directly use the |
// would repeatedly re-lock/release the monitor, we directly use the |
950 |
// unsynchronized HashIterator instead. |
// unsynchronized HashIterator instead. |
951 |
Iterator it = new HashIterator(ENTRIES); |
Iterator<Map.Entry<K, V>> it = new HashIterator<Map.Entry<K, V>>(ENTRIES); |
952 |
while (it.hasNext()) |
while (it.hasNext()) |
953 |
{ |
{ |
954 |
HashEntry entry = (HashEntry) it.next(); |
HashEntry<K, V> entry = (HashEntry<K, V>) it.next(); |
955 |
s.writeObject(entry.key); |
s.writeObject(entry.key); |
956 |
s.writeObject(entry.value); |
s.writeObject(entry.value); |
957 |
} |
} |
975 |
s.defaultReadObject(); |
s.defaultReadObject(); |
976 |
|
|
977 |
// Read and use capacity. |
// Read and use capacity. |
978 |
buckets = new HashEntry[s.readInt()]; |
buckets = new HashEntry<K, V>[s.readInt()]; |
979 |
int len = s.readInt(); |
int len = s.readInt(); |
980 |
|
|
981 |
// Read and use key/value pairs. |
// Read and use key/value pairs. |
995 |
* |
* |
996 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
997 |
*/ |
*/ |
998 |
private final class HashIterator implements Iterator |
private final class HashIterator<T> implements Iterator<T> |
999 |
{ |
{ |
1000 |
/** |
/** |
1001 |
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, |
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, |
1011 |
/** Current index in the physical hash table. */ |
/** Current index in the physical hash table. */ |
1012 |
int idx = buckets.length; |
int idx = buckets.length; |
1013 |
/** The last Entry returned by a next() call. */ |
/** The last Entry returned by a next() call. */ |
1014 |
HashEntry last; |
HashEntry<K, V> last; |
1015 |
/** |
/** |
1016 |
* The next entry that should be returned by next(). It is set to something |
* The next entry that should be returned by next(). It is set to something |
1017 |
* if we're iterating through a bucket that contains multiple linked |
* if we're iterating through a bucket that contains multiple linked |
1018 |
* entries. It is null if next() needs to find a new bucket. |
* entries. It is null if next() needs to find a new bucket. |
1019 |
*/ |
*/ |
1020 |
HashEntry next; |
HashEntry<K, V> next; |
1021 |
|
|
1022 |
/** |
/** |
1023 |
* Construct a new HashIterator with the supplied type. |
* Construct a new HashIterator with the supplied type. |
1046 |
* @throws ConcurrentModificationException if the hashtable was modified |
* @throws ConcurrentModificationException if the hashtable was modified |
1047 |
* @throws NoSuchElementException if there is none |
* @throws NoSuchElementException if there is none |
1048 |
*/ |
*/ |
1049 |
public Object next() |
public T next() |
1050 |
{ |
{ |
1051 |
if (knownMod != modCount) |
if (knownMod != modCount) |
1052 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
1053 |
if (count == 0) |
if (count == 0) |
1054 |
throw new NoSuchElementException(); |
throw new NoSuchElementException(); |
1055 |
count--; |
count--; |
1056 |
HashEntry e = next; |
HashEntry<K, V> e = next; |
1057 |
|
|
1058 |
while (e == null) |
while (e == null) |
1059 |
e = buckets[--idx]; |
e = buckets[--idx]; |
1101 |
* |
* |
1102 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
1103 |
*/ |
*/ |
1104 |
private final class Enumerator implements Enumeration |
private final class Enumerator<T> implements Enumeration<T> |
1105 |
{ |
{ |
1106 |
/** |
/** |
1107 |
* The type of this Iterator: {@link #KEYS} or {@link #VALUES}. |
* The type of this Iterator: {@link #KEYS} or {@link #VALUES}. |
1116 |
* set if we are iterating through a bucket with multiple entries, or null |
* set if we are iterating through a bucket with multiple entries, or null |
1117 |
* if we must look in the next bucket. |
* if we must look in the next bucket. |
1118 |
*/ |
*/ |
1119 |
HashEntry next; |
HashEntry<K, V> next; |
1120 |
|
|
1121 |
/** |
/** |
1122 |
* Construct the enumeration. |
* Construct the enumeration. |
1146 |
if (count == 0) |
if (count == 0) |
1147 |
throw new NoSuchElementException("Hashtable Enumerator"); |
throw new NoSuchElementException("Hashtable Enumerator"); |
1148 |
count--; |
count--; |
1149 |
HashEntry e = next; |
HashEntry<K, V> e = next; |
1150 |
|
|
1151 |
while (e == null) |
while (e == null) |
1152 |
e = buckets[--idx]; |
e = buckets[--idx]; |