37 |
// code. |
// code. |
38 |
|
|
39 |
/** |
/** |
40 |
* a class which implements a Hashtable data structure |
* A class which implements a hashtable data structure. |
41 |
* |
* |
42 |
* This implementation of Hashtable uses a hash-bucket approach. That is: |
* This implementation of Hashtable uses a hash-bucket approach. That is: |
43 |
* linear probing and rehashing is avoided; instead, each hashed value maps |
* linear probing and rehashing is avoided; instead, each hashed value maps |
45 |
* Assuming a large enough table, low enough load factor, and / or well |
* Assuming a large enough table, low enough load factor, and / or well |
46 |
* implemented hashCode() methods, Hashtable should provide O(1) |
* implemented hashCode() methods, Hashtable should provide O(1) |
47 |
* insertion, deletion, and searching of keys. Hashtable is O(n) in |
* insertion, deletion, and searching of keys. Hashtable is O(n) in |
48 |
* the worst case for all of these (if all keys has to the same bucket). |
* the worst case for all of these (if all keys hash to the same bucket). |
49 |
|
* <p> |
50 |
* |
* |
51 |
* This is a JDK-1.2 compliant implementation of Hashtable. As such, it |
* This is a JDK-1.2 compliant implementation of Hashtable. As such, it |
52 |
* belongs, partially, to the Collections framework (in that it implements |
* belongs, partially, to the Collections framework (in that it implements |
53 |
* Map). For backwards compatibility, it inherits from the obsolete and |
* Map). For backwards compatibility, it inherits from the obsolete and |
54 |
* utterly useless Dictionary class. |
* utterly useless Dictionary class. |
55 |
|
* <p> |
56 |
* |
* |
57 |
* Being a hybrid of old and new, Hashtable has methods which provide redundant |
* Being a hybrid of old and new, Hashtable has methods which provide redundant |
58 |
* capability, but with subtle and even crucial differences. |
* capability, but with subtle and even crucial differences. |
60 |
* either an Iterator (which is the JDK-1.2 way of doing things) or with an |
* either an Iterator (which is the JDK-1.2 way of doing things) or with an |
61 |
* Enumeration. The latter can end up in an undefined state if the Hashtable |
* Enumeration. The latter can end up in an undefined state if the Hashtable |
62 |
* changes while the Enumeration is open. |
* changes while the Enumeration is open. |
63 |
|
* <p> |
64 |
* |
* |
65 |
* Unlike HashMap, Hashtable does not accept `null' as a key value. |
* Unlike HashMap, Hashtable does not accept `null' as a key value. Also, |
66 |
|
* all accesses are synchronized: in a single thread environment, this is |
67 |
|
* expensive, but in a multi-thread environment, this saves you the effort |
68 |
|
* of extra synchronization. |
69 |
* |
* |
70 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
71 |
* @author Warren Levy |
* @author Warren Levy |
72 |
* @author Bryce McKinlay |
* @author Bryce McKinlay |
73 |
* @author Eric Blake <ebb9@email.byu.edu> |
* @author Eric Blake <ebb9@email.byu.edu> |
74 |
|
* @see HashMap |
75 |
|
* @see TreeMap |
76 |
|
* @see IdentityHashMap |
77 |
|
* @see LinkedHashMap |
78 |
|
* @since 1.0 |
79 |
*/ |
*/ |
80 |
public class Hashtable extends Dictionary |
public class Hashtable extends Dictionary |
81 |
implements Map, Cloneable, Serializable |
implements Map, Cloneable, Serializable |
82 |
{ |
{ |
83 |
/** Default number of buckets. This is the value the JDK 1.3 uses. Some |
/** Default number of buckets. This is the value the JDK 1.3 uses. Some |
84 |
* early documentation specified this value as 101. That is incorrect. */ |
* early documentation specified this value as 101. That is incorrect. |
85 |
private static final int DEFAULT_CAPACITY = 11; |
*/ |
86 |
|
private static final int DEFAULT_CAPACITY = 11; |
87 |
/** The default load factor; this is explicitly specified by the spec. */ |
/** The default load factor; this is explicitly specified by the spec. */ |
88 |
private static final float DEFAULT_LOAD_FACTOR = 0.75f; |
private static final float DEFAULT_LOAD_FACTOR = 0.75f; |
89 |
|
|
90 |
|
/** |
91 |
|
* Compatible with JDK 1.0+. |
92 |
|
*/ |
93 |
private static final long serialVersionUID = 1421746759512286392L; |
private static final long serialVersionUID = 1421746759512286392L; |
94 |
|
|
95 |
/** |
/** |
96 |
* The rounded product of the capacity and the load factor; when the number |
* The rounded product of the capacity and the load factor; when the number |
97 |
* of elements exceeds the threshold, the Hashtable calls <pre>rehash()</pre>. |
* of elements exceeds the threshold, the Hashtable calls |
98 |
|
* <pre>rehash()</pre>. |
99 |
* @serial |
* @serial |
100 |
*/ |
*/ |
101 |
int threshold; |
int threshold; |
103 |
/** Load factor of this Hashtable: used in computing the threshold. |
/** Load factor of this Hashtable: used in computing the threshold. |
104 |
* @serial |
* @serial |
105 |
*/ |
*/ |
106 |
float loadFactor = DEFAULT_LOAD_FACTOR; |
float loadFactor; |
107 |
|
|
108 |
/** |
/** |
109 |
* Array containing the actual key-value mappings |
* Array containing the actual key-value mappings |
110 |
*/ |
*/ |
111 |
transient Entry[] buckets; |
transient HashEntry[] buckets; |
112 |
|
|
113 |
/** |
/** |
114 |
* counts the number of modifications this Hashtable has undergone, used |
* Counts the number of modifications this Hashtable has undergone, used |
115 |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
116 |
*/ |
*/ |
117 |
transient int modCount; |
transient int modCount; |
118 |
|
|
119 |
/** the size of this Hashtable: denotes the number of key-value pairs */ |
/** The size of this Hashtable: denotes the number of key-value pairs */ |
120 |
transient int size; |
transient int size; |
121 |
|
|
122 |
/** |
/** |
124 |
* pair. A Hashtable Entry is identical to a HashMap Entry, except that |
* pair. A Hashtable Entry is identical to a HashMap Entry, except that |
125 |
* `null' is not allowed for keys and values. |
* `null' is not allowed for keys and values. |
126 |
*/ |
*/ |
127 |
static class Entry extends BasicMapEntry |
static class HashEntry extends BasicMapEntry |
128 |
{ |
{ |
129 |
Entry next; |
/** The next entry in the linked list */ |
130 |
|
HashEntry next; |
131 |
Entry(Object key, Object value) |
|
132 |
|
/** |
133 |
|
* Simple constructor. |
134 |
|
* @param key the key, already guaranteed non-null |
135 |
|
* @param value the value, already guaranteed non-null |
136 |
|
*/ |
137 |
|
HashEntry(Object key, Object value) |
138 |
{ |
{ |
139 |
super(key, value); |
super(key, value); |
140 |
} |
} |
141 |
|
|
142 |
|
/** |
143 |
|
* Resets the value. |
144 |
|
* @param newValue the new value |
145 |
|
* @return the prior value |
146 |
|
* @throws NullPointerException if <code>newVal</code> is null |
147 |
|
*/ |
148 |
public final Object setValue(Object newVal) |
public final Object setValue(Object newVal) |
149 |
{ |
{ |
150 |
if (newVal == null) |
// Throw a NullPointerException with minimal bytecode |
151 |
throw new NullPointerException(); |
newVal.getClass(); |
152 |
return super.setValue(newVal); |
return super.setValue(newVal); |
153 |
} |
} |
|
|
|
|
Entry copy() |
|
|
{ |
|
|
Entry e = new Entry(key, value); |
|
|
if (next != null) |
|
|
e.next = next.copy(); |
|
|
return e; |
|
|
} |
|
154 |
} |
} |
155 |
|
|
156 |
/** |
/** |
157 |
* construct a new Hashtable with the default capacity (11) and the default |
* Construct a new Hashtable with the default capacity (11) and the default |
158 |
* load factor (0.75). |
* load factor (0.75). |
159 |
*/ |
*/ |
160 |
public Hashtable() |
public Hashtable() |
163 |
} |
} |
164 |
|
|
165 |
/** |
/** |
166 |
* construct a new Hashtable from the given Map |
* Construct a new Hashtable from the given Map, with initial capacity |
167 |
|
* the greater of the size of m or the default of 11. |
168 |
|
* <p> |
169 |
* |
* |
170 |
* every element in Map t will be put into this new Hashtable |
* Every element in Map m will be put into this new Hashtable |
171 |
* |
* |
172 |
* @param t a Map whose key / value pairs will be put into |
* @param m a Map whose key / value pairs will be put into |
173 |
* the new Hashtable. <b>NOTE: key / value pairs |
* the new Hashtable. <b>NOTE: key / value pairs |
174 |
* are not cloned in this constructor</b> |
* are not cloned in this constructor</b> |
175 |
|
* @throws NullPointerException if m is null, or if m contains a mapping |
176 |
|
* to or from `null'. |
177 |
|
* @since 1.2 |
178 |
*/ |
*/ |
179 |
public Hashtable(Map m) |
public Hashtable(Map m) |
180 |
{ |
{ |
183 |
} |
} |
184 |
|
|
185 |
/** |
/** |
186 |
* construct a new Hashtable with a specific inital capacity |
* Construct a new Hashtable with a specific inital capacity and |
187 |
* |
* default load factor of 0.75. |
|
* @param initialCapacity the initial capacity of this Hashtable (>=0) |
|
188 |
* |
* |
189 |
* @throws IllegalArgumentException if (initialCapacity < 0) |
* @param initialCapacity the initial capacity of this Hashtable (>=0) |
190 |
|
* @throws IllegalArgumentException if (initialCapacity < 0) |
191 |
*/ |
*/ |
192 |
public Hashtable(int initialCapacity) throws IllegalArgumentException |
public Hashtable(int initialCapacity) |
193 |
{ |
{ |
194 |
this(initialCapacity, DEFAULT_LOAD_FACTOR); |
this(initialCapacity, DEFAULT_LOAD_FACTOR); |
195 |
} |
} |
196 |
|
|
197 |
/** |
/** |
198 |
* construct a new Hashtable with a specific inital capacity and load factor |
* Construct a new Hashtable with a specific initial capacity and |
199 |
|
* load factor. |
200 |
* |
* |
201 |
* @param initialCapacity the initial capacity (>=0) |
* @param initialCapacity the initial capacity (>=0) |
202 |
* @param loadFactor the load factor |
* @param loadFactor the load factor (>0, not NaN) |
203 |
* |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
204 |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
* ! (initialLoadFactor > 0.0) |
|
* (initialLoadFactor <= 0.0) |
|
205 |
*/ |
*/ |
206 |
public Hashtable(int initialCapacity, float loadFactor) |
public Hashtable(int initialCapacity, float loadFactor) |
|
throws IllegalArgumentException |
|
207 |
{ |
{ |
208 |
if (initialCapacity < 0) |
if (initialCapacity < 0) |
209 |
throw new IllegalArgumentException("Illegal Initial Capacity: " |
throw new IllegalArgumentException("Illegal Capacity: " |
210 |
+ initialCapacity); |
+ initialCapacity); |
211 |
if (loadFactor <= 0) |
if (! (loadFactor > 0)) // check for NaN too |
212 |
throw new IllegalArgumentException("Illegal Load Factor: " + loadFactor); |
throw new IllegalArgumentException("Illegal Load: " + loadFactor); |
213 |
|
|
214 |
if (initialCapacity == 0) |
if (initialCapacity == 0) |
215 |
initialCapacity = 1; |
initialCapacity = 1; |
216 |
buckets = new Entry[initialCapacity]; |
buckets = new HashEntry[initialCapacity]; |
217 |
this.loadFactor = loadFactor; |
this.loadFactor = loadFactor; |
218 |
this.threshold = (int) (initialCapacity * loadFactor); |
this.threshold = (int) (initialCapacity * loadFactor); |
219 |
} |
} |
220 |
|
|
221 |
/** Returns the number of key-value mappings currently in this Map */ |
/** |
222 |
public int size() |
* Returns the number of key-value mappings currently in this hashtable. |
223 |
|
* @return the size |
224 |
|
*/ |
225 |
|
public synchronized int size() |
226 |
{ |
{ |
227 |
return size; |
return size; |
228 |
} |
} |
229 |
|
|
230 |
/** returns true if there are no key-value mappings currently in this Map */ |
/** |
231 |
public boolean isEmpty() |
* Returns true if there are no key-value mappings currently in this table. |
232 |
|
* @return <code>size() == 0</code> |
233 |
|
*/ |
234 |
|
public synchronized boolean isEmpty() |
235 |
{ |
{ |
236 |
return size == 0; |
return size == 0; |
237 |
} |
} |
238 |
|
|
239 |
|
/** |
240 |
|
* Return an enumeration of the keys of this table. |
241 |
|
* @return the keys |
242 |
|
* @see #elements() |
243 |
|
* @see #keySet() |
244 |
|
*/ |
245 |
public synchronized Enumeration keys() |
public synchronized Enumeration keys() |
246 |
{ |
{ |
247 |
return new Enumerator(Enumerator.KEYS); |
return new Enumerator(Enumerator.KEYS); |
248 |
} |
} |
249 |
|
|
250 |
|
/** |
251 |
|
* Return an enumeration of the values of this table. |
252 |
|
* @return the values |
253 |
|
* @see #keys() |
254 |
|
* @see #values() |
255 |
|
*/ |
256 |
public synchronized Enumeration elements() |
public synchronized Enumeration elements() |
257 |
{ |
{ |
258 |
return new Enumerator(Enumerator.VALUES); |
return new Enumerator(Enumerator.VALUES); |
259 |
} |
} |
260 |
|
|
261 |
/** |
/** |
262 |
* returns true if this Hashtable contains a value <pre>o</pre>, |
* Returns true if this Hashtable contains a value <pre>o</pre>, |
263 |
* such that <pre>o.equals(value)</pre>. |
* such that <pre>o.equals(value)</pre>. This is the same as |
264 |
|
* <code>containsValue()</code>, and is O(n). |
265 |
|
* <p> |
266 |
* |
* |
267 |
* Note: this is one of the <i>old</i> Hashtable methods which does |
* Note: this is one of the <i>old</i> Hashtable methods which does |
268 |
* not like null values; it throws NullPointerException if the |
* not like null values; it throws NullPointerException if the |
269 |
* supplied parameter is null. |
* supplied parameter is null. |
270 |
* |
* |
271 |
* @param value the value to search for in this Hashtable |
* @param value the value to search for in this Hashtable |
272 |
* |
* @return true if at least one key maps to the value |
273 |
* @throws NullPointerException if <pre>value</pre> is null |
* @throws NullPointerException if <pre>value</pre> is null |
274 |
|
* @see #containsValue(Object) |
275 |
|
* @see #containsKey(Object) |
276 |
*/ |
*/ |
277 |
public synchronized boolean contains(Object value) |
public synchronized boolean contains(Object value) |
278 |
{ |
{ |
279 |
// if Hashtable is empty, this method doesn't dereference |
// Check if value is null with minimal bytecode, in case |
280 |
// `value' anywhere, so check for it explicitly. |
// Hashtable is empty. |
281 |
if (value == null) |
value.getClass(); |
282 |
throw new NullPointerException(); |
|
283 |
for (int i = 0; i < buckets.length; i++) |
for (int i = 0; i < buckets.length; i++) |
284 |
{ |
{ |
285 |
Entry e = buckets[i]; |
HashEntry e = buckets[i]; |
286 |
while (e != null) |
while (e != null) |
287 |
{ |
{ |
288 |
if (value.equals(e.value)) |
if (value.equals(e.value)) |
289 |
return true; |
return true; |
290 |
e = e.next; |
e = e.next; |
291 |
} |
} |
292 |
} |
} |
293 |
return false; |
return false; |
294 |
} |
} |
295 |
|
|
296 |
/** |
/** |
297 |
* returns true if this Hashtable contains a value <pre>o</pre>, such that |
* Returns true if this Hashtable contains a value <pre>o</pre>, such that |
298 |
* <pre>o.equals(value)</pre>. |
* <pre>o.equals(value)</pre>. This is the new API for the old |
299 |
|
* <code>contains()</code>. |
300 |
* |
* |
301 |
* @param value the value to search for in this Hashtable |
* @param value the value to search for in this Hashtable |
302 |
* |
* @return true if at least one key maps to the value |
303 |
* @throws NullPointerException if <pre>value</pre> is null |
* @throws NullPointerException if <pre>value</pre> is null |
304 |
|
* @see #contains(Object) |
305 |
|
* @see #containsKey(Object) |
306 |
|
* @since 1.2 |
307 |
*/ |
*/ |
308 |
public boolean containsValue(Object value) |
public boolean containsValue(Object value) |
309 |
{ |
{ |
311 |
} |
} |
312 |
|
|
313 |
/** |
/** |
314 |
* returns true if the supplied object equals (<pre>equals()</pre>) a key |
* Returns true if the supplied object (<pre>equals()</pre>) a key |
315 |
* in this Hashtable |
* in this Hashtable |
316 |
* |
* |
317 |
* @param key the key to search for in this Hashtable |
* @param key the key to search for in this Hashtable |
318 |
|
* @return true if the key is in the table |
319 |
|
* @throws NullPointerException if key is null |
320 |
|
* @see #containsValue(Object) |
321 |
*/ |
*/ |
322 |
public synchronized boolean containsKey(Object key) |
public synchronized boolean containsKey(Object key) |
323 |
{ |
{ |
324 |
int idx = hash(key); |
int idx = hash(key); |
325 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
326 |
while (e != null) |
while (e != null) |
327 |
{ |
{ |
328 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
329 |
return true; |
return true; |
330 |
e = e.next; |
e = e.next; |
331 |
} |
} |
332 |
return false; |
return false; |
333 |
} |
} |
334 |
|
|
335 |
/** |
/** |
336 |
* return the value in this Hashtable associated with the supplied key, or <pre>null</pre> |
* Return the value in this Hashtable associated with the supplied key, |
337 |
* if the key maps to nothing |
* or <pre>null</pre> if the key maps to nothing |
338 |
* |
* |
339 |
* @param key the key for which to fetch an associated value |
* @param key the key for which to fetch an associated value |
340 |
|
* @return what the key maps to, if present |
341 |
|
* @throws NullPointerException if key is null |
342 |
|
* @see #put(Object, Object) |
343 |
*/ |
*/ |
344 |
public synchronized Object get(Object key) |
public synchronized Object get(Object key) |
345 |
{ |
{ |
346 |
int idx = hash(key); |
int idx = hash(key); |
347 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
348 |
while (e != null) |
while (e != null) |
349 |
{ |
{ |
350 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
351 |
return e.value; |
return e.value; |
352 |
e = e.next; |
e = e.next; |
353 |
} |
} |
354 |
return null; |
return null; |
355 |
} |
} |
356 |
|
|
357 |
/** |
/** |
358 |
* puts the supplied value into the Map, mapped by the supplied key |
* Puts the supplied value into the Map, mapped by the supplied key. |
359 |
|
* Neither parameter may be null. The value may be retrieved by any |
360 |
|
* object which <code>equals()</code> this key. |
361 |
* |
* |
362 |
* @param key the key used to locate the value |
* @param key the key used to locate the value |
363 |
* @param value the value to be stored in the table |
* @param value the value to be stored in the table |
364 |
|
* @return the prior mapping of the key, or null if there was none |
365 |
|
* @throws NullPointerException if key or value is null |
366 |
|
* @see #get(Object) |
367 |
|
* @see Object#equals(Object) |
368 |
*/ |
*/ |
369 |
public synchronized Object put(Object key, Object value) |
public synchronized Object put(Object key, Object value) |
370 |
{ |
{ |
371 |
modCount++; |
modCount++; |
372 |
int idx = hash(key); |
int idx = hash(key); |
373 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
374 |
|
|
375 |
// Hashtable does not accept null values. This method doesn't dereference |
// Check if value is null with minimal bytecode, since |
376 |
// `value' anywhere, so check for it explicitly. |
// Hashtable does not accept null values. |
377 |
if (value == null) |
value.getClass(); |
|
throw new NullPointerException(); |
|
378 |
|
|
379 |
while (e != null) |
while (e != null) |
380 |
{ |
{ |
381 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
382 |
{ |
{ |
383 |
Object r = e.value; |
Object r = e.value; |
384 |
e.value = value; |
e.value = value; |
385 |
return r; |
return r; |
386 |
} |
} |
387 |
else |
else |
388 |
{ |
{ |
389 |
e = e.next; |
e = e.next; |
390 |
} |
} |
391 |
} |
} |
392 |
|
|
393 |
// At this point, we know we need to add a new entry. |
// At this point, we know we need to add a new entry. |
394 |
if (++size > threshold) |
if (++size > threshold) |
395 |
{ |
{ |
396 |
rehash(); |
rehash(); |
397 |
// Need a new hash value to suit the bigger table. |
// Need a new hash value to suit the bigger table. |
398 |
idx = hash(key); |
idx = hash(key); |
399 |
} |
} |
400 |
|
|
401 |
e = new Entry(key, value); |
e = new HashEntry(key, value); |
402 |
|
|
403 |
e.next = buckets[idx]; |
e.next = buckets[idx]; |
404 |
buckets[idx] = e; |
buckets[idx] = e; |
405 |
|
|
406 |
return null; |
return null; |
407 |
} |
} |
408 |
|
|
409 |
/** |
/** |
410 |
* removes from the table and returns the value which is mapped by the |
* Removes from the table and returns the value which is mapped by the |
411 |
* supplied key; if the key maps to nothing, then the table remains |
* supplied key. If the key maps to nothing, then the table remains |
412 |
* unchanged, and <pre>null</pre> is returned |
* unchanged, and <pre>null</pre> is returned. |
413 |
* |
* |
414 |
* @param key the key used to locate the value to remove |
* @param key the key used to locate the value to remove |
415 |
|
* @return whatever the key mapped to, if present |
416 |
|
* @throws NullPointerException if key is null |
417 |
*/ |
*/ |
418 |
public synchronized Object remove(Object key) |
public synchronized Object remove(Object key) |
419 |
{ |
{ |
420 |
modCount++; |
modCount++; |
421 |
int idx = hash(key); |
int idx = hash(key); |
422 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
423 |
Entry last = null; |
HashEntry last = null; |
424 |
|
|
425 |
while (e != null) |
while (e != null) |
426 |
{ |
{ |
427 |
if (key.equals(e.key)) |
if (key.equals(e.key)) |
428 |
{ |
{ |
429 |
if (last == null) |
if (last == null) |
430 |
buckets[idx] = e.next; |
buckets[idx] = e.next; |
431 |
else |
else |
432 |
last.next = e.next; |
last.next = e.next; |
433 |
size--; |
size--; |
434 |
return e.value; |
return e.value; |
435 |
} |
} |
436 |
last = e; |
last = e; |
437 |
e = e.next; |
e = e.next; |
438 |
} |
} |
439 |
return null; |
return null; |
440 |
} |
} |
441 |
|
|
442 |
|
/** |
443 |
|
* Copies all elements of the given map into this hashtable. However, no |
444 |
|
* mapping can contain null as key or value. If this table already has |
445 |
|
* a mapping for a key, the new mapping replaces the current one. |
446 |
|
* |
447 |
|
* @param m the map to be hashed into this |
448 |
|
* @throws NullPointerException if m is null, or contains null keys or values |
449 |
|
*/ |
450 |
public synchronized void putAll(Map m) |
public synchronized void putAll(Map m) |
451 |
{ |
{ |
452 |
int msize = m.size(); |
int msize = m.size(); |
453 |
Iterator itr = m.entrySet().iterator(); |
Iterator itr = m.entrySet().iterator(); |
454 |
|
|
455 |
for (int i=0; i < msize; i++) |
for (int i = 0; i < msize; i++) |
456 |
{ |
{ |
457 |
Map.Entry e = (Map.Entry) itr.next(); |
Entry e = (Entry) itr.next(); |
458 |
// Optimize in case the Entry is one of our own. |
// Optimize in case the Entry is one of our own. |
459 |
if (e instanceof BasicMapEntry) |
if (e instanceof BasicMapEntry) |
460 |
{ |
{ |
461 |
BasicMapEntry entry = (BasicMapEntry) e; |
BasicMapEntry entry = (BasicMapEntry) e; |
462 |
put(entry.key, entry.value); |
put(entry.key, entry.value); |
463 |
} |
} |
464 |
else |
else |
465 |
{ |
{ |
466 |
put(e.getKey(), e.getValue()); |
put(e.getKey(), e.getValue()); |
467 |
} |
} |
468 |
} |
} |
469 |
} |
} |
470 |
|
|
471 |
|
/** |
472 |
|
* Clears the hashtable so it has no keys. This is O(1). |
473 |
|
*/ |
474 |
public synchronized void clear() |
public synchronized void clear() |
475 |
{ |
{ |
476 |
modCount++; |
modCount++; |
477 |
buckets = new Entry[buckets.length]; |
buckets = new HashEntry[buckets.length]; |
478 |
size = 0; |
size = 0; |
479 |
} |
} |
480 |
|
|
481 |
/** |
/** |
482 |
* returns a shallow clone of this Hashtable (i.e. the Map itself is cloned, |
* Returns a shallow clone of this Hashtable. The Map itself is cloned, |
483 |
* but its contents are not) |
* but its contents are not. This is O(n). |
484 |
|
* |
485 |
|
* @return the clone |
486 |
*/ |
*/ |
487 |
public synchronized Object clone() |
public synchronized Object clone() |
488 |
{ |
{ |
494 |
catch (CloneNotSupportedException x) |
catch (CloneNotSupportedException x) |
495 |
{ |
{ |
496 |
} |
} |
497 |
copy.buckets = new Entry[buckets.length]; |
copy.buckets = new HashEntry[buckets.length]; |
498 |
|
|
499 |
for (int i=0; i < buckets.length; i++) |
for (int i = 0; i < buckets.length; i++) |
500 |
{ |
{ |
501 |
Entry e = buckets[i]; |
HashEntry e = buckets[i]; |
502 |
if (e != null) |
HashEntry last = null; |
503 |
copy.buckets[i] = e.copy(); |
|
504 |
|
while (e != null) |
505 |
|
{ |
506 |
|
if (last == null) |
507 |
|
{ |
508 |
|
last = new HashEntry(e.key, e.value); |
509 |
|
copy.buckets[i] = last; |
510 |
|
} |
511 |
|
else |
512 |
|
{ |
513 |
|
last.next = new HashEntry(e.key, e.value); |
514 |
|
last = last.next; |
515 |
|
} |
516 |
|
e = e.next; |
517 |
|
} |
518 |
} |
} |
519 |
return copy; |
return copy; |
520 |
} |
} |
521 |
|
|
522 |
|
/** |
523 |
|
* Converts this Hashtable to a String, surrounded by braces (<pre>'{'</pre> |
524 |
|
* and <pre>'}'</pre>), key/value pairs listed with an equals sign between, |
525 |
|
* (<pre>'='</pre>), and pairs separated by comma and space |
526 |
|
* (<pre>", "</pre>). |
527 |
|
* <p> |
528 |
|
* |
529 |
|
* NOTE: if the <code>toString()</code> method of any key or value |
530 |
|
* throws an exception, this will fail for the same reason. |
531 |
|
* |
532 |
|
* @return the string representation |
533 |
|
*/ |
534 |
public synchronized String toString() |
public synchronized String toString() |
535 |
{ |
{ |
536 |
Iterator entries = entrySet().iterator(); |
// Since we are already synchronized, and entrySet().iterator() |
537 |
|
// would repeatedly re-lock/release the monitor, we directly use the |
538 |
|
// unsynchronized HashIterator instead. |
539 |
|
Iterator entries = new HashIterator(HashIterator.ENTRIES); |
540 |
StringBuffer r = new StringBuffer("{"); |
StringBuffer r = new StringBuffer("{"); |
541 |
for (int pos = 0; pos < size; pos++) |
for (int pos = 0; pos < size; pos++) |
542 |
{ |
{ |
543 |
r.append(entries.next()); |
r.append(entries.next()); |
544 |
if (pos < size - 1) |
if (pos < size - 1) |
545 |
r.append(", "); |
r.append(", "); |
546 |
} |
} |
547 |
r.append("}"); |
r.append("}"); |
548 |
return r.toString(); |
return r.toString(); |
549 |
} |
} |
550 |
|
|
551 |
/** returns a "set view" of this Hashtable's keys */ |
/** |
552 |
|
* Returns a "set view" of this Hashtable's keys. The set is backed by |
553 |
|
* the hashtable, so changes in one show up in the other. The set supports |
554 |
|
* element removal, but not element addition. The set is properly |
555 |
|
* synchronized on the original hashtable. The set will throw a |
556 |
|
* {@link NullPointerException} if null is passed to <code>contains</code>, |
557 |
|
* <code>remove</code>, or related methods. |
558 |
|
* |
559 |
|
* @return a set view of the keys |
560 |
|
* @see #values() |
561 |
|
* @see #entrySet() |
562 |
|
* @since 1.2 |
563 |
|
*/ |
564 |
public Set keySet() |
public Set keySet() |
565 |
{ |
{ |
566 |
// Create a synchronized AbstractSet with custom implementations of those |
// Create a synchronized AbstractSet with custom implementations of those |
571 |
{ |
{ |
572 |
return size; |
return size; |
573 |
} |
} |
574 |
|
|
575 |
public Iterator iterator() |
public Iterator iterator() |
576 |
{ |
{ |
577 |
return new HashIterator(HashIterator.KEYS); |
return new HashIterator(HashIterator.KEYS); |
578 |
} |
} |
579 |
|
|
580 |
public void clear() |
public void clear() |
581 |
{ |
{ |
582 |
Hashtable.this.clear(); |
Hashtable.this.clear(); |
586 |
{ |
{ |
587 |
return Hashtable.this.containsKey(o); |
return Hashtable.this.containsKey(o); |
588 |
} |
} |
589 |
|
|
590 |
public boolean remove(Object o) |
public boolean remove(Object o) |
591 |
{ |
{ |
592 |
return (Hashtable.this.remove(o) != null); |
return (Hashtable.this.remove(o) != null); |
593 |
} |
} |
594 |
}; |
}; |
595 |
|
|
596 |
return Collections.synchronizedSet(r); |
// We must specify the correct object to synchronize upon, hence the |
597 |
|
// use of a non-public API |
598 |
|
return new Collections.SynchronizedSet(this, r); |
599 |
} |
} |
600 |
|
|
601 |
/** Returns a "collection view" (or "bag view") of this Hashtable's values. |
|
602 |
*/ |
/** |
603 |
|
* Returns a "collection view" (or "bag view") of this Hashtable's values. |
604 |
|
* The collection is backed by the hashtable, so changes in one show up |
605 |
|
* in the other. The collection supports element removal, but not element |
606 |
|
* addition. The collection is properly synchronized on the original |
607 |
|
* hashtable. The collection will throw a {@link NullPointerException} |
608 |
|
* if null is passed to <code>contains</code> or related methods, but not |
609 |
|
* if passed to <code>remove</code> or related methods. |
610 |
|
* |
611 |
|
* @return a bag view of the values |
612 |
|
* @see #keySet() |
613 |
|
* @see #entrySet() |
614 |
|
* @since 1.2 |
615 |
|
*/ |
616 |
public Collection values() |
public Collection values() |
617 |
{ |
{ |
618 |
// 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 |
623 |
{ |
{ |
624 |
return size; |
return size; |
625 |
} |
} |
626 |
|
|
627 |
public Iterator iterator() |
public Iterator iterator() |
628 |
{ |
{ |
629 |
return new HashIterator(HashIterator.VALUES); |
return new HashIterator(HashIterator.VALUES); |
630 |
} |
} |
631 |
|
|
632 |
public void clear() |
public void clear() |
633 |
{ |
{ |
634 |
Hashtable.this.clear(); |
Hashtable.this.clear(); |
635 |
} |
} |
636 |
|
|
637 |
|
// Override this so that we check for null |
638 |
|
public boolean contains(Object o) |
639 |
|
{ |
640 |
|
return Hashtable.this.contains(o); |
641 |
|
} |
642 |
}; |
}; |
643 |
|
|
644 |
return Collections.synchronizedCollection(r); |
// We must specify the correct object to synchronize upon, hence the |
645 |
|
// use of a non-public API |
646 |
|
return new Collections.SynchronizedCollection(this, r); |
647 |
} |
} |
648 |
|
|
649 |
/** Returns a "set view" of this Hashtable's entries. */ |
/** |
650 |
|
* Returns a "set view" of this Hashtable's entries. The set is backed by |
651 |
|
* the hashtable, so changes in one show up in the other. The set supports |
652 |
|
* element removal, but not element addition. The set is properly |
653 |
|
* synchronized on the original hashtable. The set will throw a |
654 |
|
* {@link NullPointerException} if the Map.Entry passed to |
655 |
|
* <code>contains</code>, <code>remove</code>, or related methods returns |
656 |
|
* null for <code>getKey</code>, but not if the Map.Entry is null or |
657 |
|
* returns null for <code>getValue</code>. |
658 |
|
* <p> |
659 |
|
* |
660 |
|
* Note that the iterators for all three views, from keySet(), entrySet(), |
661 |
|
* and values(), traverse the hashtable in the same sequence. |
662 |
|
* |
663 |
|
* @return a set view of the entries |
664 |
|
* @see #keySet() |
665 |
|
* @see #values() |
666 |
|
* @see Map.Entry |
667 |
|
* @since 1.2 |
668 |
|
*/ |
669 |
public Set entrySet() |
public Set entrySet() |
670 |
{ |
{ |
671 |
// Create an AbstractSet with custom implementations of those methods that |
// Create an AbstractSet with custom implementations of those methods that |
676 |
{ |
{ |
677 |
return size; |
return size; |
678 |
} |
} |
679 |
|
|
680 |
public Iterator iterator() |
public Iterator iterator() |
681 |
{ |
{ |
682 |
return new HashIterator(HashIterator.ENTRIES); |
return new HashIterator(HashIterator.ENTRIES); |
683 |
} |
} |
684 |
|
|
685 |
public void clear() |
public void clear() |
686 |
{ |
{ |
687 |
Hashtable.this.clear(); |
Hashtable.this.clear(); |
689 |
|
|
690 |
public boolean contains(Object o) |
public boolean contains(Object o) |
691 |
{ |
{ |
692 |
if (!(o instanceof Map.Entry)) |
if (!(o instanceof Entry)) |
693 |
return false; |
return false; |
694 |
Map.Entry me = (Map.Entry) o; |
Entry me = (Entry) o; |
695 |
Entry e = getEntry(me); |
HashEntry e = getEntry(me); |
696 |
return (e != null); |
return (e != null); |
697 |
} |
} |
698 |
|
|
699 |
public boolean remove(Object o) |
public boolean remove(Object o) |
700 |
{ |
{ |
701 |
if (!(o instanceof Map.Entry)) |
if (!(o instanceof Entry)) |
702 |
return false; |
return false; |
703 |
Map.Entry me = (Map.Entry) o; |
Entry me = (Entry) o; |
704 |
Entry e = getEntry(me); |
HashEntry e = getEntry(me); |
705 |
if (e != null) |
if (e != null) |
706 |
{ |
{ |
707 |
Hashtable.this.remove(e.key); |
Hashtable.this.remove(e.key); |
708 |
return true; |
return true; |
709 |
} |
} |
710 |
return false; |
return false; |
711 |
} |
} |
712 |
}; |
}; |
713 |
|
|
714 |
return Collections.synchronizedSet(r); |
// We must specify the correct object to synchronize upon, hence the |
715 |
|
// use of a non-public API |
716 |
|
return new Collections.SynchronizedSet(this, r); |
717 |
} |
} |
718 |
|
|
719 |
/** returns true if this Hashtable equals the supplied Object <pre>o</pre>; |
/** |
720 |
* that is: |
* Returns true if this Hashtable equals the supplied Object <pre>o</pre>. |
721 |
|
* As specified by Map, this is: |
722 |
* <pre> |
* <pre> |
723 |
* if (o instanceof Map) |
* (o instanceof Map) && entrySet().equals(((Map) o).entrySet()); |
724 |
* and |
* </pre> |
725 |
* o.keySet().equals(keySet()) |
* |
726 |
* and |
* @param o the object to compare to |
727 |
* for each key in o.keySet(), o.get(key).equals(get(key)) |
* @return true if o is an equal map |
728 |
*</pre> |
* @since 1.2 |
729 |
*/ |
*/ |
730 |
public boolean equals(Object o) |
public boolean equals(Object o) |
731 |
{ |
{ |
732 |
|
// no need to synchronize, entrySet().equals() does that |
733 |
if (o == this) |
if (o == this) |
734 |
return true; |
return true; |
735 |
if (!(o instanceof Map)) |
if (!(o instanceof Map)) |
736 |
return false; |
return false; |
737 |
|
|
738 |
Map m = (Map) o; |
return entrySet().equals(((Map) o).entrySet()); |
|
Set s = m.entrySet(); |
|
|
Iterator itr = entrySet().iterator(); |
|
|
|
|
|
if (m.size() != size) |
|
|
return false; |
|
|
|
|
|
for (int pos = 0; pos < size; pos++) |
|
|
{ |
|
|
if (!s.contains(itr.next())) |
|
|
return false; |
|
|
} |
|
|
return true; |
|
739 |
} |
} |
740 |
|
|
741 |
/** a Map's hashCode is the sum of the hashCodes of all of its |
/** |
742 |
Map.Entry objects */ |
* Returns the hashCode for this Hashtable. As specified by Map, this is |
743 |
public int hashCode() |
* the sum of the hashCodes of all of its Map.Entry objects |
744 |
|
* |
745 |
|
* @return the sum of the hashcodes of the entries |
746 |
|
* @since 1.2 |
747 |
|
*/ |
748 |
|
public synchronized int hashCode() |
749 |
{ |
{ |
750 |
|
// Since we are already synchronized, and entrySet().iterator() |
751 |
|
// would repeatedly re-lock/release the monitor, we directly use the |
752 |
|
// unsynchronized HashIterator instead. |
753 |
|
Iterator itr = new HashIterator(HashIterator.ENTRIES); |
754 |
int hashcode = 0; |
int hashcode = 0; |
|
Iterator itr = entrySet().iterator(); |
|
755 |
for (int pos = 0; pos < size; pos++) |
for (int pos = 0; pos < size; pos++) |
756 |
{ |
{ |
757 |
hashcode += itr.next().hashCode(); |
hashcode += itr.next().hashCode(); |
758 |
} |
} |
759 |
return hashcode; |
return hashcode; |
760 |
} |
} |
761 |
|
|
762 |
/** Return an index in the buckets array for `key' based on its hashCode() */ |
/** |
763 |
|
* Helper method that returns an index in the buckets array for `key' |
764 |
|
* based on its hashCode(). |
765 |
|
* |
766 |
|
* @param key the key |
767 |
|
* @return the bucket number |
768 |
|
* @throws NullPointerException if key is null |
769 |
|
*/ |
770 |
private int hash(Object key) |
private int hash(Object key) |
771 |
{ |
{ |
772 |
return Math.abs(key.hashCode() % buckets.length); |
return Math.abs(key.hashCode() % buckets.length); |
773 |
} |
} |
774 |
|
|
775 |
private Entry getEntry(Map.Entry me) |
/** |
776 |
|
* Helper method for entrySet(), which matches both key and value |
777 |
|
* simultaneously. |
778 |
|
* |
779 |
|
* @param me the entry to match |
780 |
|
* @return the matching entry, if found, or null |
781 |
|
* @throws NullPointerException if me.getKey() returns null |
782 |
|
* @see #entrySet() |
783 |
|
*/ |
784 |
|
private HashEntry getEntry(Entry me) |
785 |
{ |
{ |
786 |
|
if (me == null) |
787 |
|
return null; |
788 |
int idx = hash(me.getKey()); |
int idx = hash(me.getKey()); |
789 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
790 |
while (e != null) |
while (e != null) |
791 |
{ |
{ |
792 |
if (e.equals(me)) |
if (e.equals(me)) |
793 |
return e; |
return e; |
794 |
e = e.next; |
e = e.next; |
795 |
} |
} |
796 |
return null; |
return null; |
797 |
} |
} |
798 |
|
|
799 |
/** |
/** |
800 |
* increases the size of the Hashtable and rehashes all keys to new array |
* Increases the size of the Hashtable and rehashes all keys to new array |
801 |
* indices; this is called when the addition of a new value would cause |
* indices; this is called when the addition of a new value would cause |
802 |
* size() > threshold. Note that the existing Entry objects are reused in |
* size() > threshold. Note that the existing Entry objects are reused in |
803 |
* the new hash table. |
* the new hash table. |
804 |
|
* <p> |
805 |
|
* |
806 |
|
* This is not specified, but the new size is twice the current size plus |
807 |
|
* one; this number is not always prime, unfortunately. |
808 |
*/ |
*/ |
809 |
protected void rehash() |
protected void rehash() |
810 |
{ |
{ |
811 |
Entry[] oldBuckets = buckets; |
HashEntry[] oldBuckets = buckets; |
812 |
|
|
813 |
int newcapacity = (buckets.length * 2) + 1; |
int newcapacity = (buckets.length * 2) + 1; |
814 |
threshold = (int) (newcapacity * loadFactor); |
threshold = (int) (newcapacity * loadFactor); |
815 |
buckets = new Entry[newcapacity]; |
buckets = new HashEntry[newcapacity]; |
816 |
|
|
817 |
for (int i = 0; i < oldBuckets.length; i++) |
for (int i = 0; i < oldBuckets.length; i++) |
818 |
{ |
{ |
819 |
Entry e = oldBuckets[i]; |
HashEntry e = oldBuckets[i]; |
820 |
while (e != null) |
while (e != null) |
821 |
{ |
{ |
822 |
int idx = hash(e.key); |
int idx = hash(e.key); |
823 |
Entry dest = buckets[idx]; |
HashEntry dest = buckets[idx]; |
824 |
|
|
825 |
if (dest != null) |
if (dest != null) |
826 |
{ |
{ |
827 |
while (dest.next != null) |
while (dest.next != null) |
828 |
dest = dest.next; |
dest = dest.next; |
829 |
dest.next = e; |
dest.next = e; |
830 |
} |
} |
831 |
else |
else |
832 |
{ |
{ |
833 |
buckets[idx] = e; |
buckets[idx] = e; |
834 |
} |
} |
835 |
|
|
836 |
Entry next = e.next; |
HashEntry next = e.next; |
837 |
e.next = null; |
e.next = null; |
838 |
e = next; |
e = next; |
839 |
} |
} |
840 |
} |
} |
841 |
} |
} |
842 |
|
|
843 |
/** |
/** |
844 |
* Serializes this object to the given stream. |
* Serializes this object to the given stream. |
845 |
* @serialdata the <i>capacity</i>(int) that is the length of the |
* |
846 |
* bucket array, the <i>size</i>(int) of the hash map are emitted |
* @param s the stream to write to |
847 |
* first. They are followed by size entries, each consisting of |
* @throws IOException if the underlying stream fails |
848 |
* a key (Object) and a value (Object). |
* @serialData the <i>capacity</i>(int) that is the length of the |
849 |
|
* bucket array, the <i>size</i>(int) of the hash map |
850 |
|
* are emitted first. They are followed by size entries, |
851 |
|
* each consisting of a key (Object) and a value (Object). |
852 |
*/ |
*/ |
853 |
private void writeObject(ObjectOutputStream s) throws IOException |
private synchronized void writeObject(ObjectOutputStream s) |
854 |
|
throws IOException |
855 |
{ |
{ |
856 |
// the threshold and loadFactor fields |
// Write the threshold and loadFactor fields. |
857 |
s.defaultWriteObject(); |
s.defaultWriteObject(); |
858 |
|
|
859 |
s.writeInt(buckets.length); |
s.writeInt(buckets.length); |
860 |
s.writeInt(size); |
s.writeInt(size); |
861 |
Iterator it = entrySet().iterator(); |
// Since we are already synchronized, and entrySet().iterator() |
862 |
|
// would repeatedly re-lock/release the monitor, we directly use the |
863 |
|
// unsynchronized HashIterator instead. |
864 |
|
Iterator it = new HashIterator(HashIterator.ENTRIES); |
865 |
while (it.hasNext()) |
while (it.hasNext()) |
866 |
{ |
{ |
867 |
Map.Entry entry = (Map.Entry) it.next(); |
HashEntry entry = (HashEntry) it.next(); |
868 |
s.writeObject(entry.getKey()); |
s.writeObject(entry.key); |
869 |
s.writeObject(entry.getValue()); |
s.writeObject(entry.value); |
870 |
} |
} |
871 |
} |
} |
872 |
|
|
873 |
/** |
/** |
874 |
* Deserializes this object from the given stream. |
* Deserializes this object from the given stream. |
875 |
* @serialdata the <i>capacity</i>(int) that is the length of the |
* |
876 |
* bucket array, the <i>size</i>(int) of the hash map are emitted |
* @param s the stream to read from |
877 |
* first. They are followed by size entries, each consisting of |
* @throws ClassNotFoundException if the underlying stream fails |
878 |
* a key (Object) and a value (Object). |
* @throws IOException if the underlying stream fails |
879 |
|
* @serialData the <i>capacity</i>(int) that is the length of the |
880 |
|
* bucket array, the <i>size</i>(int) of the hash map |
881 |
|
* are emitted first. They are followed by size entries, |
882 |
|
* each consisting of a key (Object) and a value (Object). |
883 |
*/ |
*/ |
884 |
private void readObject(ObjectInputStream s) |
private void readObject(ObjectInputStream s) |
885 |
throws IOException, ClassNotFoundException |
throws IOException, ClassNotFoundException |
886 |
{ |
{ |
887 |
// the threshold and loadFactor fields |
// Read the threshold and loadFactor fields. |
888 |
s.defaultReadObject(); |
s.defaultReadObject(); |
889 |
|
|
890 |
int capacity = s.readInt(); |
int capacity = s.readInt(); |
891 |
int len = s.readInt(); |
int len = s.readInt(); |
892 |
size = 0; |
size = 0; |
893 |
modCount = 0; |
modCount = 0; |
894 |
buckets = new Entry[capacity]; |
buckets = new HashEntry[capacity]; |
895 |
|
|
896 |
for (int i = 0; i < len; i++) |
for (int i = 0; i < len; i++) |
897 |
{ |
{ |
898 |
Object key = s.readObject(); |
Object key = s.readObject(); |
899 |
Object value = s.readObject(); |
Object value = s.readObject(); |
900 |
put(key, value); |
put(key, value); |
901 |
} |
} |
902 |
} |
} |
903 |
|
|
904 |
/** |
/** |
905 |
* a class which implements the Iterator interface and is used for |
* A class which implements the Iterator interface and is used for |
906 |
* iterating over Hashtables; |
* iterating over Hashtables. |
907 |
* this implementation is parameterized to give a sequential view of |
* This implementation is parameterized to give a sequential view of |
908 |
* keys, values, or entries; it also allows the removal of elements, |
* keys, values, or entries; it also allows the removal of elements, |
909 |
* as per the Javasoft spec. |
* as per the Javasoft spec. Note that it is not synchronized; this is |
910 |
|
* a performance enhancer since it is never exposed externally and is |
911 |
|
* only used within synchronized blocks above. |
912 |
* |
* |
913 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
914 |
*/ |
*/ |
915 |
class HashIterator implements Iterator |
class HashIterator implements Iterator |
916 |
{ |
{ |
917 |
static final int KEYS = 0, |
/** "enum" of iterator types. */ |
918 |
VALUES = 1, |
static final int KEYS = 0; |
919 |
ENTRIES = 2; |
/** "enum" of iterator types. */ |
920 |
|
static final int VALUES = 1; |
921 |
// The type of this Iterator: KEYS, VALUES, or ENTRIES. |
/** "enum" of iterator types. */ |
922 |
|
static final int ENTRIES = 2; |
923 |
|
|
924 |
|
/** |
925 |
|
* The type of this Iterator: {@link KEYS}, {@link VALUES}, |
926 |
|
* or {@link ENTRIES}. |
927 |
|
*/ |
928 |
int type; |
int type; |
929 |
// The number of modifications to the backing Hashtable that we know about. |
/** |
930 |
|
* The number of modifications to the backing Hashtable that we know about. |
931 |
|
*/ |
932 |
int knownMod; |
int knownMod; |
933 |
// The total number of elements returned by next(). Used to determine if |
/** |
934 |
// there are more elements remaining. |
* The total number of elements returned by next(). Used to determine if |
935 |
|
* there are more elements remaining. |
936 |
|
*/ |
937 |
int count; |
int count; |
938 |
// Current index in the physical hash table. |
/** Current index in the physical hash table. */ |
939 |
int idx; |
int idx; |
940 |
// The last Entry returned by a next() call. |
/** The last Entry returned by a next() call. */ |
941 |
Entry last; |
HashEntry last; |
942 |
// The next entry that should be returned by next(). It is set to something |
/** |
943 |
// if we're iterating through a bucket that contains multiple linked |
* The next entry that should be returned by next(). It is set to something |
944 |
// entries. It is null if next() needs to find a new bucket. |
* if we're iterating through a bucket that contains multiple linked |
945 |
Entry next; |
* entries. It is null if next() needs to find a new bucket. |
946 |
|
*/ |
947 |
|
HashEntry next; |
948 |
|
|
949 |
/* Construct a new HashIterator with the supplied type: |
/** |
950 |
KEYS, VALUES, or ENTRIES */ |
* Construct a new HashIterator with the supplied type. |
951 |
|
* @param type {@link KEYS}, {@link VALUES}, or {@link ENTRIES} |
952 |
|
*/ |
953 |
HashIterator(int type) |
HashIterator(int type) |
954 |
{ |
{ |
955 |
this.type = type; |
this.type = type; |
958 |
idx = buckets.length; |
idx = buckets.length; |
959 |
} |
} |
960 |
|
|
961 |
/** returns true if the Iterator has more elements */ |
/** |
962 |
|
* Returns true if the Iterator has more elements. |
963 |
|
* @return true if there are more elements |
964 |
|
* @throws ConcurrentModificationException if the hashtable was modified |
965 |
|
*/ |
966 |
public boolean hasNext() |
public boolean hasNext() |
967 |
{ |
{ |
968 |
if (knownMod != Hashtable.this.modCount) |
if (knownMod != Hashtable.this.modCount) |
969 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
970 |
return count < size; |
return count < size; |
971 |
} |
} |
972 |
|
|
973 |
/** Returns the next element in the Iterator's sequential view. */ |
/** |
974 |
|
* Returns the next element in the Iterator's sequential view. |
975 |
|
* @return the next element |
976 |
|
* @throws ConcurrentModificationException if the hashtable was modified |
977 |
|
* @throws NoSuchElementException if there is none |
978 |
|
*/ |
979 |
public Object next() |
public Object next() |
980 |
{ |
{ |
981 |
if (knownMod != Hashtable.this.modCount) |
if (knownMod != Hashtable.this.modCount) |
982 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
983 |
if (count == size) |
if (count == size) |
984 |
throw new NoSuchElementException(); |
throw new NoSuchElementException(); |
985 |
count++; |
count++; |
986 |
Entry e = null; |
HashEntry e = null; |
987 |
if (next != null) |
if (next != null) |
988 |
e = next; |
e = next; |
989 |
|
|
990 |
while (e == null) |
while (e == null) |
991 |
{ |
{ |
992 |
e = buckets[--idx]; |
e = buckets[--idx]; |
993 |
} |
} |
994 |
|
|
995 |
next = e.next; |
next = e.next; |
996 |
last = e; |
last = e; |
1004 |
/** |
/** |
1005 |
* Removes from the backing Hashtable the last element which was fetched |
* Removes from the backing Hashtable the last element which was fetched |
1006 |
* with the <pre>next()</pre> method. |
* with the <pre>next()</pre> method. |
1007 |
|
* @throws ConcurrentModificationException if the hashtable was modified |
1008 |
|
* @throws IllegalStateException if called when there is no last element |
1009 |
*/ |
*/ |
1010 |
public void remove() |
public void remove() |
1011 |
{ |
{ |
1012 |
if (knownMod != Hashtable.this.modCount) |
if (knownMod != Hashtable.this.modCount) |
1013 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
1014 |
if (last == null) |
if (last == null) |
1015 |
{ |
throw new IllegalStateException(); |
1016 |
throw new IllegalStateException(); |
|
1017 |
} |
Hashtable.this.remove(last.key); |
1018 |
else |
knownMod++; |
1019 |
{ |
count--; |
1020 |
Hashtable.this.remove(last.key); |
last = null; |
|
knownMod++; |
|
|
count--; |
|
|
last = null; |
|
|
} |
|
1021 |
} |
} |
1022 |
} |
} |
1023 |
|
|
1034 |
* the "Java Class Libraries" book infers that modifications to the |
* the "Java Class Libraries" book infers that modifications to the |
1035 |
* hashtable during enumeration causes indeterminate results. Don't do it! |
* hashtable during enumeration causes indeterminate results. Don't do it! |
1036 |
* |
* |
1037 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
1038 |
*/ |
*/ |
1039 |
class Enumerator implements Enumeration |
class Enumerator implements Enumeration |
1040 |
{ |
{ |
1041 |
|
/** "enum" of iterator types. */ |
1042 |
static final int KEYS = 0; |
static final int KEYS = 0; |
1043 |
|
/** "enum" of iterator types. */ |
1044 |
static final int VALUES = 1; |
static final int VALUES = 1; |
1045 |
|
|
1046 |
|
/** |
1047 |
|
* The type of this Iterator: {@link KEYS} or {@link VALUES}. |
1048 |
|
*/ |
1049 |
int type; |
int type; |
1050 |
// current index in the physical hash table. |
/** Current index in the physical hash table. */ |
1051 |
int idx; |
int idx; |
1052 |
// the last Entry returned by nextEntry(). |
/** The last Entry returned by nextEntry(). */ |
1053 |
Entry last; |
HashEntry last; |
1054 |
// Entry which will be returned by the next nextElement() call. |
/** Entry which will be returned by the next nextElement() call. */ |
1055 |
Entry next; |
HashEntry next; |
1056 |
|
|
1057 |
|
/** |
1058 |
|
* Construct the enumeration. |
1059 |
|
* @param type either {@link KEYS} or {@link VALUES}. |
1060 |
|
*/ |
1061 |
Enumerator(int type) |
Enumerator(int type) |
1062 |
{ |
{ |
1063 |
this.type = type; |
this.type = type; |
1064 |
this.idx = buckets.length; |
this.idx = buckets.length; |
1065 |
} |
} |
1066 |
|
|
1067 |
private Entry nextEntry() |
/** |
1068 |
|
* Helper method to find the next entry. |
1069 |
|
* @return the next entry, or null |
1070 |
|
*/ |
1071 |
|
private HashEntry nextEntry() |
1072 |
{ |
{ |
1073 |
Entry e = null; |
HashEntry e = null; |
1074 |
|
|
1075 |
if (last != null) |
if (last != null) |
1076 |
e = last.next; |
e = last.next; |
1077 |
|
|
1078 |
while (e == null && idx > 0) |
while (e == null && idx > 0) |
1079 |
{ |
{ |
1080 |
e = buckets[--idx]; |
e = buckets[--idx]; |
1081 |
} |
} |
1082 |
last = e; |
last = e; |
1083 |
return e; |
return e; |
1084 |
} |
} |
1085 |
|
|
1086 |
|
/** |
1087 |
|
* Checks whether more elements remain in the enumeration. |
1088 |
|
* @return true if nextElement() will not fail. |
1089 |
|
*/ |
1090 |
public boolean hasMoreElements() |
public boolean hasMoreElements() |
1091 |
{ |
{ |
1092 |
if (next != null) |
if (next != null) |
1095 |
return (next != null); |
return (next != null); |
1096 |
} |
} |
1097 |
|
|
1098 |
|
/** |
1099 |
|
* Returns the next element. |
1100 |
|
* @return the next element |
1101 |
|
* @throws NoSuchElementException if there is none. |
1102 |
|
*/ |
1103 |
public Object nextElement() |
public Object nextElement() |
1104 |
{ |
{ |
1105 |
Entry e = null; |
HashEntry e; |
1106 |
if (next != null) |
if (next != null) |
1107 |
{ |
{ |
1108 |
e = next; |
e = next; |
1109 |
next = null; |
next = null; |
1110 |
} |
} |
1111 |
else |
else |
1112 |
e = nextEntry(); |
e = nextEntry(); |
1113 |
if (e == null) |
if (e == null) |