53 |
* <p> |
* <p> |
54 |
* |
* |
55 |
* Under ideal circumstances (no collisions), HashMap offers O(1) |
* Under ideal circumstances (no collisions), HashMap offers O(1) |
56 |
* performance on most operations (<pre>containsValue()</pre> is, |
* performance on most operations (<code>containsValue()</code> is, |
57 |
* of course, O(n)). In the worst case (all keys map to the same |
* of course, O(n)). In the worst case (all keys map to the same |
58 |
* hash code -- very unlikely), most operations are O(n). |
* hash code -- very unlikely), most operations are O(n). |
59 |
* <p> |
* <p> |
60 |
* |
* |
61 |
* HashMap is part of the JDK1.2 Collections API. It differs from |
* HashMap is part of the JDK1.2 Collections API. It differs from |
62 |
* Hashtable in that it accepts the null key and null values, and it |
* Hashtable in that it accepts the null key and null values, and it |
63 |
* does not support "Enumeration views." |
* does not support "Enumeration views." Also, it is not synchronized; |
64 |
|
* if you plan to use it in multiple threads, consider using:<br> |
65 |
|
* <code>Map m = Collections.synchronizedMap(new HashMap(...));</code> |
66 |
* <p> |
* <p> |
67 |
* |
* |
68 |
* The iterators are <i>fail-fast</i>, meaning that any structural |
* The iterators are <i>fail-fast</i>, meaning that any structural |
83 |
* @see IdentityHashMap |
* @see IdentityHashMap |
84 |
* @see Hashtable |
* @see Hashtable |
85 |
* @since 1.2 |
* @since 1.2 |
86 |
|
* @status updated to 1.4 |
87 |
*/ |
*/ |
88 |
public class HashMap extends AbstractMap |
public class HashMap extends AbstractMap |
89 |
implements Map, Cloneable, Serializable |
implements Map, Cloneable, Serializable |
91 |
/** |
/** |
92 |
* 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 |
93 |
* early documentation specified this value as 101. That is incorrect. |
* early documentation specified this value as 101. That is incorrect. |
94 |
|
* Package visible for use by HashSet. |
95 |
*/ |
*/ |
96 |
static final int DEFAULT_CAPACITY = 11; |
static final int DEFAULT_CAPACITY = 11; |
97 |
|
|
98 |
/** |
/** |
99 |
* The default load factor; this is explicitly specified by the spec. |
* The default load factor; this is explicitly specified by the spec. |
100 |
|
* Package visible for use by HashSet. |
101 |
*/ |
*/ |
102 |
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
103 |
|
|
|
/** "enum" of iterator types. */ |
|
|
static final int KEYS = 0, |
|
|
VALUES = 1, |
|
|
ENTRIES = 2; |
|
|
|
|
104 |
/** |
/** |
105 |
* Compatible with JDK 1.2. |
* Compatible with JDK 1.2. |
106 |
*/ |
*/ |
108 |
|
|
109 |
/** |
/** |
110 |
* 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 |
111 |
* of elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>. |
* of elements exceeds the threshold, the HashMap calls |
112 |
* @serial |
* <code>rehash()</code>. |
113 |
|
* @serial the threshold for rehashing |
114 |
*/ |
*/ |
115 |
int threshold; |
private int threshold; |
116 |
|
|
117 |
/** |
/** |
118 |
* Load factor of this HashMap: used in computing the threshold. |
* Load factor of this HashMap: used in computing the threshold. |
119 |
* @serial |
* Package visible for use by HashSet. |
120 |
|
* @serial the load factor |
121 |
*/ |
*/ |
122 |
final float loadFactor; |
final float loadFactor; |
123 |
|
|
124 |
/** |
/** |
125 |
* Array containing the actual key-value mappings. |
* Array containing the actual key-value mappings. |
126 |
|
* Package visible for use by nested and subclasses. |
127 |
*/ |
*/ |
128 |
transient HashEntry[] buckets; |
transient HashEntry[] buckets; |
129 |
|
|
130 |
/** |
/** |
131 |
* Counts the number of modifications this HashMap has undergone, used |
* Counts the number of modifications this HashMap has undergone, used |
132 |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
133 |
|
* Package visible for use by nested and subclasses. |
134 |
*/ |
*/ |
135 |
transient int modCount; |
transient int modCount; |
136 |
|
|
137 |
/** |
/** |
138 |
* The size of this HashMap: denotes the number of key-value pairs. |
* The size of this HashMap: denotes the number of key-value pairs. |
139 |
|
* Package visible for use by nested and subclasses. |
140 |
*/ |
*/ |
141 |
transient int size; |
transient int size; |
142 |
|
|
143 |
/** |
/** |
144 |
|
* The cache for {@link #entrySet()}. |
145 |
|
*/ |
146 |
|
private transient Set entries; |
147 |
|
|
148 |
|
/** |
149 |
* 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 |
150 |
* pair. This is extended again in LinkedHashMap. See {@link clone()} |
* pair. Package visible for use by subclass. |
151 |
* for why this must be Cloneable. |
* |
152 |
|
* @author Eric Blake <ebb9@email.byu.edu> |
153 |
*/ |
*/ |
154 |
static class HashEntry extends BasicMapEntry implements Cloneable |
static class HashEntry extends BasicMapEntry |
155 |
{ |
{ |
156 |
/** The next entry in the linked list. */ |
/** |
157 |
|
* The next entry in the linked list. Package visible for use by subclass. |
158 |
|
*/ |
159 |
HashEntry next; |
HashEntry next; |
160 |
|
|
161 |
/** |
/** |
171 |
/** |
/** |
172 |
* Called when this entry is removed from the map. This version simply |
* Called when this entry is removed from the map. This version simply |
173 |
* returns the value, but in LinkedHashMap, it must also do bookkeeping. |
* returns the value, but in LinkedHashMap, it must also do bookkeeping. |
174 |
* @return the value of this key as it is removed. |
* |
175 |
|
* @return the value of this key as it is removed |
176 |
*/ |
*/ |
177 |
Object cleanup() |
Object cleanup() |
178 |
{ |
{ |
196 |
* |
* |
197 |
* Every element in Map m will be put into this new HashMap. |
* Every element in Map m will be put into this new HashMap. |
198 |
* |
* |
199 |
* @param m a Map whose key / value pairs will be put into |
* @param m a Map whose key / value pairs will be put into the new HashMap. |
200 |
* the new HashMap. <b>NOTE: key / value pairs |
* <b>NOTE: key / value pairs are not cloned in this constructor.</b> |
|
* are not cloned in this constructor.</b> |
|
201 |
* @throws NullPointerException if m is null |
* @throws NullPointerException if m is null |
202 |
*/ |
*/ |
203 |
public HashMap(Map m) |
public HashMap(Map m) |
210 |
* Construct a new HashMap with a specific inital capacity and |
* Construct a new HashMap with a specific inital capacity and |
211 |
* default load factor of 0.75. |
* default load factor of 0.75. |
212 |
* |
* |
213 |
* @param initialCapacity the initial capacity of this HashMap (>=0) |
* @param initialCapacity the initial capacity of this HashMap (>=0) |
214 |
* @throws IllegalArgumentException if (initialCapacity < 0) |
* @throws IllegalArgumentException if (initialCapacity < 0) |
215 |
*/ |
*/ |
216 |
public HashMap(int initialCapacity) |
public HashMap(int initialCapacity) |
217 |
{ |
{ |
221 |
/** |
/** |
222 |
* Construct a new HashMap with a specific inital capacity and load factor. |
* Construct a new HashMap with a specific inital capacity and load factor. |
223 |
* |
* |
224 |
* @param initialCapacity the initial capacity (>=0) |
* @param initialCapacity the initial capacity (>=0) |
225 |
* @param loadFactor the load factor (>0, not NaN) |
* @param loadFactor the load factor (> 0, not NaN) |
226 |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
227 |
* ! (loadFactor > 0.0) |
* ! (loadFactor > 0.0) |
228 |
*/ |
*/ |
229 |
public HashMap(int initialCapacity, float loadFactor) |
public HashMap(int initialCapacity, float loadFactor) |
230 |
{ |
{ |
242 |
} |
} |
243 |
|
|
244 |
/** |
/** |
245 |
* Returns the number of kay-value mappings currently in this Map |
* Returns the number of kay-value mappings currently in this Map. |
246 |
|
* |
247 |
* @return the size |
* @return the size |
248 |
*/ |
*/ |
249 |
public int size() |
public int size() |
252 |
} |
} |
253 |
|
|
254 |
/** |
/** |
255 |
* Returns true if there are no key-value mappings currently in this Map |
* Returns true if there are no key-value mappings currently in this Map. |
256 |
|
* |
257 |
* @return <code>size() == 0</code> |
* @return <code>size() == 0</code> |
258 |
*/ |
*/ |
259 |
public boolean isEmpty() |
public boolean isEmpty() |
262 |
} |
} |
263 |
|
|
264 |
/** |
/** |
265 |
* Returns true if this HashMap contains a value <pre>o</pre>, such that |
* Return the value in this HashMap associated with the supplied key, |
266 |
* <pre>o.equals(value)</pre>. |
* or <code>null</code> if the key maps to nothing. NOTE: Since the value |
267 |
|
* could also be null, you must use containsKey to see if this key |
268 |
|
* actually maps to something. |
269 |
* |
* |
270 |
* @param value the value to search for in this HashMap |
* @param key the key for which to fetch an associated value |
271 |
* @return true if at least one key maps to the value |
* @return what the key maps to, if present |
272 |
|
* @see #put(Object, Object) |
273 |
|
* @see #containsKey(Object) |
274 |
*/ |
*/ |
275 |
public boolean containsValue(Object value) |
public Object get(Object key) |
276 |
{ |
{ |
277 |
for (int i = buckets.length - 1; i >= 0; i--) |
int idx = hash(key); |
278 |
|
HashEntry e = buckets[idx]; |
279 |
|
while (e != null) |
280 |
{ |
{ |
281 |
HashEntry e = buckets[i]; |
if (equals(key, e.key)) |
282 |
while (e != null) |
return e.value; |
283 |
{ |
e = e.next; |
|
if (value == null ? e.value == null : value.equals(e.value)) |
|
|
return true; |
|
|
e = e.next; |
|
|
} |
|
284 |
} |
} |
285 |
return false; |
return null; |
286 |
} |
} |
287 |
|
|
288 |
/** |
/** |
289 |
* Returns true if the supplied object <pre>equals()</pre> a key |
* Returns true if the supplied object <code>equals()</code> a key |
290 |
* in this HashMap. |
* in this HashMap. |
291 |
* |
* |
292 |
* @param key the key to search for in this HashMap |
* @param key the key to search for in this HashMap |
299 |
HashEntry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
300 |
while (e != null) |
while (e != null) |
301 |
{ |
{ |
302 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (equals(key, e.key)) |
303 |
return true; |
return true; |
304 |
e = e.next; |
e = e.next; |
305 |
} |
} |
307 |
} |
} |
308 |
|
|
309 |
/** |
/** |
|
* Return the value in this HashMap associated with the supplied key, |
|
|
* or <pre>null</pre> if the key maps to nothing. NOTE: Since the value |
|
|
* could also be null, you must use containsKey to see if this key |
|
|
* actually maps to something. |
|
|
* |
|
|
* @param key the key for which to fetch an associated value |
|
|
* @return what the key maps to, if present |
|
|
* @see #put(Object, Object) |
|
|
* @see #containsKey(Object) |
|
|
*/ |
|
|
public Object get(Object key) |
|
|
{ |
|
|
int idx = hash(key); |
|
|
HashEntry e = buckets[idx]; |
|
|
while (e != null) |
|
|
{ |
|
|
if (key == null ? e.key == null : key.equals(e.key)) |
|
|
return e.value; |
|
|
e = e.next; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
/** |
|
310 |
* Puts the supplied value into the Map, mapped by the supplied key. |
* Puts the supplied value into the Map, mapped by the supplied key. |
311 |
* The value may be retrieved by any object which <code>equals()</code> |
* The value may be retrieved by any object which <code>equals()</code> |
312 |
* this key. NOTE: Since the prior value could also be null, you must |
* this key. NOTE: Since the prior value could also be null, you must |
321 |
*/ |
*/ |
322 |
public Object put(Object key, Object value) |
public Object put(Object key, Object value) |
323 |
{ |
{ |
|
modCount++; |
|
324 |
int idx = hash(key); |
int idx = hash(key); |
325 |
HashEntry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
326 |
|
|
327 |
while (e != null) |
while (e != null) |
328 |
{ |
{ |
329 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (equals(key, e.key)) |
330 |
// Must use this method for necessary bookkeeping in LinkedHashMap. |
// Must use this method for necessary bookkeeping in LinkedHashMap. |
331 |
return e.setValue(value); |
return e.setValue(value); |
332 |
else |
else |
334 |
} |
} |
335 |
|
|
336 |
// At this point, we know we need to add a new entry. |
// At this point, we know we need to add a new entry. |
337 |
|
modCount++; |
338 |
if (++size > threshold) |
if (++size > threshold) |
339 |
{ |
{ |
340 |
rehash(); |
rehash(); |
348 |
} |
} |
349 |
|
|
350 |
/** |
/** |
351 |
* Helper method for put, that creates and adds a new Entry. This is |
* Copies all elements of the given map into this hashtable. If this table |
352 |
* overridden in LinkedHashMap for bookkeeping purposes. |
* already has a mapping for a key, the new mapping replaces the current |
353 |
|
* one. |
354 |
* |
* |
355 |
* @param key the key of the new Entry |
* @param m the map to be hashed into this |
|
* @param value the value |
|
|
* @param idx the index in buckets where the new Entry belongs |
|
|
* @param callRemove Whether to call the removeEldestEntry method. |
|
|
* @see #put(Object, Object) |
|
356 |
*/ |
*/ |
357 |
void addEntry(Object key, Object value, int idx, boolean callRemove) |
public void putAll(Map m) |
358 |
{ |
{ |
359 |
HashEntry e = new HashEntry(key, value); |
Iterator itr = m.entrySet().iterator(); |
360 |
|
|
361 |
e.next = buckets[idx]; |
for (int msize = m.size(); msize > 0; msize--) |
362 |
buckets[idx] = e; |
{ |
363 |
|
Map.Entry e = (Map.Entry) itr.next(); |
364 |
|
// Optimize in case the Entry is one of our own. |
365 |
|
if (e instanceof BasicMapEntry) |
366 |
|
{ |
367 |
|
BasicMapEntry entry = (BasicMapEntry) e; |
368 |
|
put(entry.key, entry.value); |
369 |
|
} |
370 |
|
else |
371 |
|
{ |
372 |
|
put(e.getKey(), e.getValue()); |
373 |
|
} |
374 |
|
} |
375 |
} |
} |
376 |
|
|
377 |
/** |
/** |
378 |
* Removes from the HashMap and returns the value which is mapped by the |
* Removes from the HashMap and returns the value which is mapped by the |
379 |
* supplied key. If the key maps to nothing, then the HashMap remains |
* supplied key. If the key maps to nothing, then the HashMap remains |
380 |
* unchanged, and <pre>null</pre> is returned. NOTE: Since the value |
* unchanged, and <code>null</code> is returned. NOTE: Since the value |
381 |
* could also be null, you must use containsKey to see if you are |
* could also be null, you must use containsKey to see if you are |
382 |
* actually removing a mapping. |
* actually removing a mapping. |
383 |
* |
* |
386 |
*/ |
*/ |
387 |
public Object remove(Object key) |
public Object remove(Object key) |
388 |
{ |
{ |
|
modCount++; |
|
389 |
int idx = hash(key); |
int idx = hash(key); |
390 |
HashEntry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
391 |
HashEntry last = null; |
HashEntry last = null; |
392 |
|
|
393 |
while (e != null) |
while (e != null) |
394 |
{ |
{ |
395 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (equals(key, e.key)) |
396 |
{ |
{ |
397 |
|
modCount++; |
398 |
if (last == null) |
if (last == null) |
399 |
buckets[idx] = e.next; |
buckets[idx] = e.next; |
400 |
else |
else |
410 |
} |
} |
411 |
|
|
412 |
/** |
/** |
413 |
* Copies all elements of the given map into this hashtable. If this table |
* Clears the Map so it has no keys. This is O(1). |
|
* already has a mapping for a key, the new mapping replaces the current |
|
|
* one. |
|
|
* |
|
|
* @param m the map to be hashed into this |
|
414 |
*/ |
*/ |
415 |
public void putAll(Map m) |
public void clear() |
416 |
{ |
{ |
417 |
Iterator itr = m.entrySet().iterator(); |
if (size != 0) |
|
|
|
|
for (int msize = m.size(); msize > 0; msize--) |
|
418 |
{ |
{ |
419 |
Map.Entry e = (Map.Entry) itr.next(); |
modCount++; |
420 |
// Optimize in case the Entry is one of our own. |
Arrays.fill(buckets, null); |
421 |
if (e instanceof BasicMapEntry) |
size = 0; |
|
{ |
|
|
BasicMapEntry entry = (BasicMapEntry) e; |
|
|
put(entry.key, entry.value); |
|
|
} |
|
|
else |
|
|
{ |
|
|
put(e.getKey(), e.getValue()); |
|
|
} |
|
422 |
} |
} |
423 |
} |
} |
424 |
|
|
425 |
/** |
/** |
426 |
* Clears the Map so it has no keys. This is O(1). |
* Returns true if this HashMap contains a value <code>o</code>, such that |
427 |
|
* <code>o.equals(value)</code>. |
428 |
|
* |
429 |
|
* @param value the value to search for in this HashMap |
430 |
|
* @return true if at least one key maps to the value |
431 |
|
* @see containsKey(Object) |
432 |
*/ |
*/ |
433 |
public void clear() |
public boolean containsValue(Object value) |
434 |
{ |
{ |
435 |
modCount++; |
for (int i = buckets.length - 1; i >= 0; i--) |
436 |
Arrays.fill(buckets, null); |
{ |
437 |
size = 0; |
HashEntry e = buckets[i]; |
438 |
|
while (e != null) |
439 |
|
{ |
440 |
|
if (equals(value, e.value)) |
441 |
|
return true; |
442 |
|
e = e.next; |
443 |
|
} |
444 |
|
} |
445 |
|
return false; |
446 |
} |
} |
447 |
|
|
448 |
/** |
/** |
464 |
} |
} |
465 |
copy.buckets = new HashEntry[buckets.length]; |
copy.buckets = new HashEntry[buckets.length]; |
466 |
copy.putAllInternal(this); |
copy.putAllInternal(this); |
467 |
|
copy.entries = null; |
468 |
return copy; |
return copy; |
469 |
} |
} |
470 |
|
|
479 |
*/ |
*/ |
480 |
public Set keySet() |
public Set keySet() |
481 |
{ |
{ |
482 |
// Create an AbstractSet with custom implementations of those methods that |
if (keys == null) |
483 |
// can be overridden easily and efficiently. |
// Create an AbstractSet with custom implementations of those methods |
484 |
return new AbstractSet() |
// that can be overridden easily and efficiently. |
485 |
{ |
keys = new AbstractSet() |
486 |
public int size() |
{ |
487 |
{ |
public int size() |
488 |
return size; |
{ |
489 |
} |
return size; |
490 |
|
} |
491 |
public Iterator iterator() |
|
492 |
{ |
public Iterator iterator() |
493 |
// Cannot create the iterator directly, because of LinkedHashMap. |
{ |
494 |
return HashMap.this.iterator(KEYS); |
// Cannot create the iterator directly, because of LinkedHashMap. |
495 |
} |
return HashMap.this.iterator(KEYS); |
496 |
|
} |
497 |
public void clear() |
|
498 |
{ |
public void clear() |
499 |
HashMap.this.clear(); |
{ |
500 |
} |
HashMap.this.clear(); |
501 |
|
} |
502 |
public boolean contains(Object o) |
|
503 |
{ |
public boolean contains(Object o) |
504 |
return HashMap.this.containsKey(o); |
{ |
505 |
} |
return containsKey(o); |
506 |
|
} |
507 |
public boolean remove(Object o) |
|
508 |
{ |
public boolean remove(Object o) |
509 |
// Test against the size of the HashMap to determine if anything |
{ |
510 |
// really got removed. This is neccessary because the return value of |
// Test against the size of the HashMap to determine if anything |
511 |
// HashMap.remove() is ambiguous in the null case. |
// really got removed. This is neccessary because the return value |
512 |
int oldsize = size; |
// of HashMap.remove() is ambiguous in the null case. |
513 |
HashMap.this.remove(o); |
int oldsize = size; |
514 |
return (oldsize != size); |
HashMap.this.remove(o); |
515 |
} |
return oldsize != size; |
516 |
}; |
} |
517 |
|
}; |
518 |
|
return keys; |
519 |
} |
} |
520 |
|
|
521 |
/** |
/** |
530 |
*/ |
*/ |
531 |
public Collection values() |
public Collection values() |
532 |
{ |
{ |
533 |
// We don't bother overriding many of the optional methods, as doing so |
if (values == null) |
534 |
// wouldn't provide any significant performance advantage. |
// We don't bother overriding many of the optional methods, as doing so |
535 |
return new AbstractCollection() |
// wouldn't provide any significant performance advantage. |
536 |
{ |
values = new AbstractCollection() |
537 |
public int size() |
{ |
538 |
{ |
public int size() |
539 |
return size; |
{ |
540 |
} |
return size; |
541 |
|
} |
542 |
public Iterator iterator() |
|
543 |
{ |
public Iterator iterator() |
544 |
// Cannot create the iterator directly, because of LinkedHashMap. |
{ |
545 |
return HashMap.this.iterator(VALUES); |
// Cannot create the iterator directly, because of LinkedHashMap. |
546 |
} |
return HashMap.this.iterator(VALUES); |
547 |
|
} |
548 |
public void clear() |
|
549 |
{ |
public void clear() |
550 |
HashMap.this.clear(); |
{ |
551 |
} |
HashMap.this.clear(); |
552 |
}; |
} |
553 |
|
}; |
554 |
|
return values; |
555 |
} |
} |
556 |
|
|
557 |
/** |
/** |
558 |
* Returns a "set view" of this HashMap's entries. The set is backed by |
* Returns a "set view" of this HashMap's entries. The set is backed by |
559 |
* the HashMap, so changes in one show up in the other. The set supports |
* the HashMap, so changes in one show up in the other. The set supports |
560 |
* element removal, but not element addition. |
* element removal, but not element addition.<p> |
|
* <p> |
|
561 |
* |
* |
562 |
* Note that the iterators for all three views, from keySet(), entrySet(), |
* Note that the iterators for all three views, from keySet(), entrySet(), |
563 |
* and values(), traverse the HashMap in the same sequence. |
* and values(), traverse the HashMap in the same sequence. |
569 |
*/ |
*/ |
570 |
public Set entrySet() |
public Set entrySet() |
571 |
{ |
{ |
572 |
// Create an AbstractSet with custom implementations of those methods that |
if (entries == null) |
573 |
// can be overridden easily and efficiently. |
// Create an AbstractSet with custom implementations of those methods |
574 |
return new AbstractSet() |
// that can be overridden easily and efficiently. |
575 |
{ |
entries = new AbstractSet() |
576 |
public int size() |
{ |
577 |
{ |
public int size() |
578 |
return size; |
{ |
579 |
} |
return size; |
580 |
|
} |
581 |
public Iterator iterator() |
|
582 |
{ |
public Iterator iterator() |
583 |
// Cannot create the iterator directly, because of LinkedHashMap. |
{ |
584 |
return HashMap.this.iterator(ENTRIES); |
// Cannot create the iterator directly, because of LinkedHashMap. |
585 |
} |
return HashMap.this.iterator(ENTRIES); |
586 |
|
} |
587 |
public void clear() |
|
588 |
{ |
public void clear() |
589 |
HashMap.this.clear(); |
{ |
590 |
} |
HashMap.this.clear(); |
591 |
|
} |
592 |
public boolean contains(Object o) |
|
593 |
{ |
public boolean contains(Object o) |
594 |
return getEntry(o) != null; |
{ |
595 |
} |
return getEntry(o) != null; |
596 |
|
} |
597 |
public boolean remove(Object o) |
|
598 |
{ |
public boolean remove(Object o) |
599 |
HashEntry e = getEntry(o); |
{ |
600 |
if (e != null) |
HashEntry e = getEntry(o); |
601 |
{ |
if (e != null) |
602 |
HashMap.this.remove(e.key); |
{ |
603 |
return true; |
HashMap.this.remove(e.key); |
604 |
} |
return true; |
605 |
return false; |
} |
606 |
} |
return false; |
607 |
}; |
} |
608 |
|
}; |
609 |
|
return entries; |
610 |
} |
} |
611 |
|
|
612 |
/** Helper method that returns an index in the buckets array for `key; |
/** |
613 |
* based on its hashCode(). |
* Helper method for put, that creates and adds a new Entry. This is |
614 |
|
* overridden in LinkedHashMap for bookkeeping purposes. |
615 |
* |
* |
616 |
* @param key the key |
* @param key the key of the new Entry |
617 |
* @return the bucket number |
* @param value the value |
618 |
|
* @param idx the index in buckets where the new Entry belongs |
619 |
|
* @param callRemove whether to call the removeEldestEntry method |
620 |
|
* @see #put(Object, Object) |
621 |
*/ |
*/ |
622 |
int hash(Object key) |
void addEntry(Object key, Object value, int idx, boolean callRemove) |
623 |
{ |
{ |
624 |
return (key == null) ? 0 : Math.abs(key.hashCode() % buckets.length); |
HashEntry e = new HashEntry(key, value); |
625 |
|
|
626 |
|
e.next = buckets[idx]; |
627 |
|
buckets[idx] = e; |
628 |
} |
} |
629 |
|
|
630 |
/** |
/** |
652 |
} |
} |
653 |
|
|
654 |
/** |
/** |
655 |
|
* Helper method that returns an index in the buckets array for `key' |
656 |
|
* based on its hashCode(). Package visible for use by subclasses. |
657 |
|
* |
658 |
|
* @param key the key |
659 |
|
* @return the bucket number |
660 |
|
*/ |
661 |
|
final int hash(Object key) |
662 |
|
{ |
663 |
|
return key == null ? 0 : Math.abs(key.hashCode() % buckets.length); |
664 |
|
} |
665 |
|
|
666 |
|
/** |
667 |
|
* Generates a parameterized iterator. Must be overrideable, since |
668 |
|
* LinkedHashMap iterates in a different order. |
669 |
|
* |
670 |
|
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} |
671 |
|
* @return the appropriate iterator |
672 |
|
*/ |
673 |
|
Iterator iterator(int type) |
674 |
|
{ |
675 |
|
return new HashIterator(type); |
676 |
|
} |
677 |
|
|
678 |
|
/** |
679 |
|
* A simplified, more efficient internal implementation of putAll(). The |
680 |
|
* Map constructor and clone() should not call putAll or put, in order to |
681 |
|
* be compatible with the JDK implementation with respect to subclasses. |
682 |
|
* |
683 |
|
* @param m the map to initialize this from |
684 |
|
*/ |
685 |
|
void putAllInternal(Map m) |
686 |
|
{ |
687 |
|
Iterator itr = m.entrySet().iterator(); |
688 |
|
|
689 |
|
for (int msize = m.size(); msize > 0; msize--) |
690 |
|
{ |
691 |
|
Map.Entry e = (Map.Entry) itr.next(); |
692 |
|
Object key = e.getKey(); |
693 |
|
int idx = hash(key); |
694 |
|
addEntry(key, e.getValue(), idx, false); |
695 |
|
} |
696 |
|
} |
697 |
|
|
698 |
|
/** |
699 |
* Increases the size of the HashMap and rehashes all keys to new array |
* Increases the size of the HashMap and rehashes all keys to new array |
700 |
* 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 |
701 |
* size() > threshold. Note that the existing Entry objects are reused in |
* size() > threshold. Note that the existing Entry objects are reused in |
740 |
} |
} |
741 |
|
|
742 |
/** |
/** |
|
* Generates a parameterized iterator. Must be overrideable, since |
|
|
* LinkedHashMap iterates in a different order. |
|
|
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} |
|
|
* @return the appropriate iterator |
|
|
*/ |
|
|
Iterator iterator(int type) |
|
|
{ |
|
|
return new HashIterator(type); |
|
|
} |
|
|
|
|
|
/** |
|
|
* A simplified, more efficient internal implementation of putAll(). The |
|
|
* Map constructor and clone() should not call putAll or put, in order to |
|
|
* be compatible with the JDK implementation with respect to subclasses. |
|
|
*/ |
|
|
void putAllInternal(Map m) |
|
|
{ |
|
|
Iterator itr = m.entrySet().iterator(); |
|
|
|
|
|
for (int msize = m.size(); msize > 0; msize--) |
|
|
{ |
|
|
Map.Entry e = (Map.Entry) itr.next(); |
|
|
Object key = e.getKey(); |
|
|
int idx = hash(key); |
|
|
addEntry(key, e.getValue(), idx, false); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
743 |
* Serializes this object to the given stream. |
* Serializes this object to the given stream. |
744 |
* |
* |
745 |
* @param s the stream to write to |
* @param s the stream to write to |
786 |
// Read and use capacity. |
// Read and use capacity. |
787 |
buckets = new HashEntry[s.readInt()]; |
buckets = new HashEntry[s.readInt()]; |
788 |
int len = s.readInt(); |
int len = s.readInt(); |
|
// Already happens automatically. |
|
|
// size = 0; |
|
|
// modCount = 0; |
|
789 |
|
|
790 |
// Read and use key/value pairs. |
// Read and use key/value pairs. |
791 |
for ( ; len > 0; len--) |
for ( ; len > 0; len--) |
799 |
* |
* |
800 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
801 |
*/ |
*/ |
802 |
class HashIterator implements Iterator |
private final class HashIterator implements Iterator |
803 |
{ |
{ |
804 |
/** |
/** |
805 |
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, |
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, |
806 |
* or {@link #ENTRIES}. |
* or {@link #ENTRIES}. |
807 |
*/ |
*/ |
808 |
final int type; |
private final int type; |
809 |
/** |
/** |
810 |
* The number of modifications to the backing HashMap that we know about. |
* The number of modifications to the backing HashMap that we know about. |
811 |
*/ |
*/ |
812 |
int knownMod = modCount; |
private int knownMod = modCount; |
813 |
/** The number of elements remaining to be returned by next(). */ |
/** The number of elements remaining to be returned by next(). */ |
814 |
int count = size; |
private int count = size; |
815 |
/** Current index in the physical hash table. */ |
/** Current index in the physical hash table. */ |
816 |
int idx = buckets.length; |
private int idx = buckets.length; |
817 |
/** The last Entry returned by a next() call. */ |
/** The last Entry returned by a next() call. */ |
818 |
HashEntry last; |
private HashEntry last; |
819 |
/** |
/** |
820 |
* 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 |
821 |
* if we're iterating through a bucket that contains multiple linked |
* if we're iterating through a bucket that contains multiple linked |
822 |
* entries. It is null if next() needs to find a new bucket. |
* entries. It is null if next() needs to find a new bucket. |
823 |
*/ |
*/ |
824 |
HashEntry next; |
private HashEntry next; |
825 |
|
|
826 |
/** |
/** |
827 |
* Construct a new HashIterator with the supplied type. |
* Construct a new HashIterator with the supplied type. |
866 |
last = e; |
last = e; |
867 |
if (type == VALUES) |
if (type == VALUES) |
868 |
return e.value; |
return e.value; |
869 |
else if (type == KEYS) |
if (type == KEYS) |
870 |
return e.key; |
return e.key; |
871 |
return e; |
return e; |
872 |
} |
} |
873 |
|
|
874 |
/** |
/** |
875 |
* Removes from the backing HashMap the last element which was fetched |
* Removes from the backing HashMap the last element which was fetched |
876 |
* with the <pre>next()</pre> method. |
* with the <code>next()</code> method. |
877 |
* @throws ConcurrentModificationException if the HashMap was modified |
* @throws ConcurrentModificationException if the HashMap was modified |
878 |
* @throws IllegalStateException if called when there is no last element |
* @throws IllegalStateException if called when there is no last element |
879 |
*/ |
*/ |
885 |
throw new IllegalStateException(); |
throw new IllegalStateException(); |
886 |
|
|
887 |
HashMap.this.remove(last.key); |
HashMap.this.remove(last.key); |
|
knownMod++; |
|
888 |
last = null; |
last = null; |
889 |
|
knownMod++; |
890 |
} |
} |
891 |
} |
} |
892 |
} |
} |