37 |
// a bug in here, chances are you should make a similar change to the Hashtable |
// a bug in here, chances are you should make a similar change to the Hashtable |
38 |
// code. |
// code. |
39 |
|
|
40 |
|
// NOTE: This implementation has some nasty coding style in order to |
41 |
|
// support LinkedHashMap, which extends this. |
42 |
|
|
43 |
/** |
/** |
44 |
* This class provides a hashtable-backed implementation of the |
* This class provides a hashtable-backed implementation of the |
45 |
* Map interface. |
* Map interface. |
46 |
|
* <p> |
47 |
* |
* |
48 |
* It uses a hash-bucket approach; that is, hash |
* It uses a hash-bucket approach; that is, hash collisions are handled |
49 |
* collisions are handled by linking the new node off of the |
* by linking the new node off of the pre-existing node (or list of |
50 |
* pre-existing node (or list of nodes). In this manner, techniques |
* nodes). In this manner, techniques such as linear probing (which |
51 |
* such as linear probing (which can cause primary clustering) and |
* can cause primary clustering) and rehashing (which does not fit very |
52 |
* rehashing (which does not fit very well with Java's method of |
* well with Java's method of precomputing hash codes) are avoided. |
53 |
* precomputing hash codes) are avoided. |
* <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 (<pre>containsValue()</pre> 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> |
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." |
64 |
|
* <p> |
65 |
|
* |
66 |
|
* The iterators are <i>fail-fast</i>, meaning that any structural |
67 |
|
* modification, except for <code>remove()</code> called on the iterator |
68 |
|
* itself, cause the iterator to throw a |
69 |
|
* <code>ConcurrentModificationException</code> rather than exhibit |
70 |
|
* non-deterministic behavior. |
71 |
* |
* |
72 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
73 |
* @author Jochen Hoenicke |
* @author Jochen Hoenicke |
74 |
* @author Bryce McKinlay |
* @author Bryce McKinlay |
75 |
* @author Eric Blake <ebb9@email.byu.edu> |
* @author Eric Blake <ebb9@email.byu.edu> |
76 |
|
* @see Object#hashCode() |
77 |
|
* @see Collection |
78 |
|
* @see Map |
79 |
|
* @see TreeMap |
80 |
|
* @see LinkedHashMap |
81 |
|
* @see IdentityHashMap |
82 |
|
* @see Hashtable |
83 |
|
* @since 1.2 |
84 |
*/ |
*/ |
85 |
public class HashMap extends AbstractMap |
public class HashMap extends AbstractMap |
86 |
implements Map, Cloneable, Serializable |
implements Map, Cloneable, Serializable |
87 |
{ |
{ |
88 |
/** Default number of buckets. This is the value the JDK 1.3 uses. Some |
/** |
89 |
* early documentation specified this value as 101. That is incorrect. */ |
* Default number of buckets. This is the value the JDK 1.3 uses. Some |
90 |
private static final int DEFAULT_CAPACITY = 11; |
* early documentation specified this value as 101. That is incorrect. |
91 |
/** The default load factor; this is explicitly specified by the spec. */ |
*/ |
92 |
private static final float DEFAULT_LOAD_FACTOR = 0.75f; |
static final int DEFAULT_CAPACITY = 11; |
93 |
|
|
94 |
|
/** |
95 |
|
* The default load factor; this is explicitly specified by the spec. |
96 |
|
*/ |
97 |
|
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
98 |
|
|
99 |
|
/** "enum" of iterator types. */ |
100 |
|
static final int KEYS = 0, |
101 |
|
VALUES = 1, |
102 |
|
ENTRIES = 2; |
103 |
|
|
104 |
|
/** |
105 |
|
* Compatible with JDK 1.2. |
106 |
|
*/ |
107 |
private static final long serialVersionUID = 362498820763181265L; |
private static final long serialVersionUID = 362498820763181265L; |
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 <pre>rehash()</pre>. |
112 |
* @serial |
* @serial |
113 |
*/ |
*/ |
114 |
int threshold; |
int threshold; |
115 |
|
|
116 |
/** Load factor of this HashMap: used in computing the threshold. |
/** |
117 |
|
* Load factor of this HashMap: used in computing the threshold. |
118 |
* @serial |
* @serial |
119 |
*/ |
*/ |
120 |
float loadFactor = DEFAULT_LOAD_FACTOR; |
final float loadFactor; |
121 |
|
|
122 |
/** |
/** |
123 |
* Array containing the actual key-value mappings |
* Array containing the actual key-value mappings. |
124 |
*/ |
*/ |
125 |
transient Entry[] buckets; |
transient HashEntry[] buckets; |
126 |
|
|
127 |
/** |
/** |
128 |
* counts the number of modifications this HashMap has undergone, used |
* Counts the number of modifications this HashMap has undergone, used |
129 |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
* by Iterators to know when to throw ConcurrentModificationExceptions. |
130 |
*/ |
*/ |
131 |
transient int modCount; |
transient int modCount; |
132 |
|
|
133 |
/** the size of this HashMap: denotes the number of key-value pairs */ |
/** |
134 |
|
* The size of this HashMap: denotes the number of key-value pairs. |
135 |
|
*/ |
136 |
transient int size; |
transient int size; |
137 |
|
|
138 |
/** |
/** |
139 |
* 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 |
140 |
* pair. |
* pair. This is extended again in LinkedHashMap. See {@link clone()} |
141 |
|
* for why this must be Cloneable. |
142 |
*/ |
*/ |
143 |
static class Entry extends BasicMapEntry |
static class HashEntry extends BasicMapEntry implements Cloneable |
144 |
{ |
{ |
145 |
Entry next; |
/** The next entry in the linked list. */ |
146 |
|
HashEntry next; |
147 |
Entry(Object key, Object value) |
|
148 |
|
/** |
149 |
|
* Simple constructor. |
150 |
|
* @param key the key |
151 |
|
* @param value the value |
152 |
|
*/ |
153 |
|
HashEntry(Object key, Object value) |
154 |
{ |
{ |
155 |
super(key, value); |
super(key, value); |
156 |
} |
} |
157 |
|
|
158 |
|
/** |
159 |
|
* Called when this entry is removed from the map. This version simply |
160 |
|
* returns the value, but in LinkedHashMap, it must also do bookkeeping. |
161 |
|
* @return the value of this key as it is removed |
162 |
|
*/ |
163 |
|
Object cleanup() |
164 |
|
{ |
165 |
|
return value; |
166 |
|
} |
167 |
|
|
168 |
|
/** |
169 |
|
* Clone this Entry. |
170 |
|
* @return the cloned object |
171 |
|
*/ |
172 |
|
protected Object clone() |
173 |
|
{ |
174 |
|
try |
175 |
|
{ |
176 |
|
return super.clone(); |
177 |
|
} |
178 |
|
catch (CloneNotSupportedException e) |
179 |
|
{ |
180 |
|
// This is impossible. |
181 |
|
return null; |
182 |
|
} |
183 |
|
} |
184 |
} |
} |
185 |
|
|
186 |
/** |
/** |
187 |
* construct a new HashMap with the default capacity (11) and the default |
* Construct a new HashMap with the default capacity (11) and the default |
188 |
* load factor (0.75). |
* load factor (0.75). |
189 |
*/ |
*/ |
190 |
public HashMap() |
public HashMap() |
193 |
} |
} |
194 |
|
|
195 |
/** |
/** |
196 |
* construct a new HashMap from the given Map |
* Construct a new HashMap from the given Map, with initial capacity |
197 |
* |
* the greater of the size of <code>m</code> or the default of 11. |
198 |
* every element in Map t will be put into this new HashMap |
* <p> |
199 |
* |
* |
200 |
* @param t a Map whose key / value pairs will be put into |
* Every element in Map m will be put into this new HashMap. |
201 |
* the new HashMap. <b>NOTE: key / value pairs |
* |
202 |
* are not cloned in this constructor</b> |
* @param m a Map whose key / value pairs will be put into |
203 |
|
* the new HashMap. <b>NOTE: key / value pairs |
204 |
|
* are not cloned in this constructor.</b> |
205 |
|
* @throws NullPointerException if m is null |
206 |
*/ |
*/ |
207 |
public HashMap(Map m) |
public HashMap(Map m) |
208 |
{ |
{ |
209 |
this(Math.max(m.size() * 2, DEFAULT_CAPACITY)); |
this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); |
210 |
putAll(m); |
putAll(m); |
211 |
} |
} |
212 |
|
|
213 |
/** |
/** |
214 |
* construct a new HashMap with a specific inital capacity |
* Construct a new HashMap with a specific inital capacity and |
215 |
* |
* default load factor of 0.75. |
|
* @param initialCapacity the initial capacity of this HashMap (>=0) |
|
216 |
* |
* |
217 |
* @throws IllegalArgumentException if (initialCapacity < 0) |
* @param initialCapacity the initial capacity of this HashMap (>=0) |
218 |
|
* @throws IllegalArgumentException if (initialCapacity < 0) |
219 |
*/ |
*/ |
220 |
public HashMap(int initialCapacity) throws IllegalArgumentException |
public HashMap(int initialCapacity) |
221 |
{ |
{ |
222 |
this(initialCapacity, DEFAULT_LOAD_FACTOR); |
this(initialCapacity, DEFAULT_LOAD_FACTOR); |
223 |
} |
} |
224 |
|
|
225 |
/** |
/** |
226 |
* construct a new HashMap with a specific inital capacity and load factor |
* Construct a new HashMap with a specific inital capacity and load factor. |
227 |
* |
* |
228 |
* @param initialCapacity the initial capacity (>=0) |
* @param initialCapacity the initial capacity (>=0) |
229 |
* @param loadFactor the load factor |
* @param loadFactor the load factor (>0, not NaN) |
230 |
* |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
231 |
* @throws IllegalArgumentException if (initialCapacity < 0) || |
* ! (loadFactor > 0.0) |
|
* (loadFactor <= 0) |
|
232 |
*/ |
*/ |
233 |
public HashMap(int initialCapacity, float loadFactor) |
public HashMap(int initialCapacity, float loadFactor) |
|
throws IllegalArgumentException |
|
234 |
{ |
{ |
235 |
if (initialCapacity < 0) |
if (initialCapacity < 0) |
236 |
throw new IllegalArgumentException("Illegal Initial Capacity: " |
throw new IllegalArgumentException("Illegal Capacity: " |
237 |
+ initialCapacity); |
+ initialCapacity); |
238 |
if (loadFactor <= 0) |
if (! (loadFactor > 0)) // check for NaN too |
239 |
throw new IllegalArgumentException("Illegal Load Factor: " + loadFactor); |
throw new IllegalArgumentException("Illegal Load: " + loadFactor); |
240 |
|
|
241 |
if (initialCapacity == 0) |
if (initialCapacity == 0) |
242 |
initialCapacity = 1; |
initialCapacity = 1; |
243 |
buckets = new Entry[initialCapacity]; |
buckets = new HashEntry[initialCapacity]; |
244 |
this.loadFactor = loadFactor; |
this.loadFactor = loadFactor; |
245 |
this.threshold = (int) (initialCapacity * loadFactor); |
threshold = (int) (initialCapacity * loadFactor); |
246 |
} |
} |
247 |
|
|
248 |
/** returns the number of kay-value mappings currently in this Map */ |
/** |
249 |
|
* Returns the number of kay-value mappings currently in this Map |
250 |
|
* @return the size |
251 |
|
*/ |
252 |
public int size() |
public int size() |
253 |
{ |
{ |
254 |
return size; |
return size; |
255 |
} |
} |
256 |
|
|
257 |
/** returns true if there are no key-value mappings currently in this Map */ |
/** |
258 |
|
* Returns true if there are no key-value mappings currently in this Map |
259 |
|
* @return <code>size() == 0</code> |
260 |
|
*/ |
261 |
public boolean isEmpty() |
public boolean isEmpty() |
262 |
{ |
{ |
263 |
return size == 0; |
return size == 0; |
264 |
} |
} |
265 |
|
|
266 |
/** |
/** |
267 |
* returns true if this HashMap contains a value <pre>o</pre>, such that |
* Returns true if this HashMap contains a value <pre>o</pre>, such that |
268 |
* <pre>o.equals(value)</pre>. |
* <pre>o.equals(value)</pre>. |
269 |
* |
* |
270 |
* @param value the value to search for in this Hashtable |
* @param value the value to search for in this HashMap |
271 |
|
* @return true if at least one key maps to the value |
272 |
*/ |
*/ |
273 |
public boolean containsValue(Object value) |
public boolean containsValue(Object value) |
274 |
{ |
{ |
275 |
for (int i = 0; i < buckets.length; i++) |
for (int i = buckets.length - 1; i >= 0; i--) |
276 |
{ |
{ |
277 |
Entry e = buckets[i]; |
HashEntry e = buckets[i]; |
278 |
while (e != null) |
while (e != null) |
279 |
{ |
{ |
280 |
if (value == null ? e.value == null : value.equals(e.value)) |
if (value == null ? e.value == null : value.equals(e.value)) |
281 |
return true; |
return true; |
282 |
e = e.next; |
e = e.next; |
283 |
} |
} |
284 |
} |
} |
285 |
return false; |
return false; |
286 |
} |
} |
287 |
|
|
288 |
/** |
/** |
289 |
* returns true if the supplied object equals (<pre>equals()</pre>) a key |
* Returns true if the supplied object <pre>equals()</pre> 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 |
293 |
|
* @return true if the key is in the table |
294 |
|
* @see #containsValue(Object) |
295 |
*/ |
*/ |
296 |
public boolean containsKey(Object key) |
public boolean containsKey(Object key) |
297 |
{ |
{ |
298 |
int idx = hash(key); |
int idx = hash(key); |
299 |
Entry 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 (key == null ? e.key == null : key.equals(e.key)) |
303 |
return true; |
return true; |
304 |
e = e.next; |
e = e.next; |
305 |
} |
} |
306 |
return false; |
return false; |
307 |
} |
} |
308 |
|
|
309 |
/** |
/** |
310 |
* return the value in this Hashtable associated with the supplied key, or <pre>null</pre> |
* Return the value in this HashMap associated with the supplied key, |
311 |
* if the key maps to nothing |
* or <pre>null</pre> if the key maps to nothing. NOTE: Since the value |
312 |
|
* could also be null, you must use containsKey to see if this key |
313 |
|
* actually maps to something. |
314 |
* |
* |
315 |
* @param key the key for which to fetch an associated value |
* @param key the key for which to fetch an associated value |
316 |
|
* @return what the key maps to, if present |
317 |
|
* @see #put(Object, Object) |
318 |
|
* @see #containsKey(Object) |
319 |
*/ |
*/ |
320 |
public Object get(Object key) |
public Object get(Object key) |
321 |
{ |
{ |
322 |
int idx = hash(key); |
int idx = hash(key); |
323 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
324 |
while (e != null) |
while (e != null) |
325 |
{ |
{ |
326 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (key == null ? e.key == null : key.equals(e.key)) |
327 |
return e.value; |
return e.value; |
328 |
e = e.next; |
e = e.next; |
329 |
} |
} |
330 |
return null; |
return null; |
331 |
} |
} |
332 |
|
|
333 |
/** |
/** |
334 |
* puts the supplied value into the Map, mapped by the supplied key |
* Puts the supplied value into the Map, mapped by the supplied key. |
335 |
|
* The value may be retrieved by any object which <code>equals()</code> |
336 |
|
* this key. NOTE: Since the prior value could also be null, you must |
337 |
|
* first use containsKey if you want to see if you are replacing the |
338 |
|
* key's mapping. |
339 |
* |
* |
340 |
* @param key the HashMap key used to locate the value |
* @param key the key used to locate the value |
341 |
* @param value the value to be stored in the HashMap |
* @param value the value to be stored in the HashMap |
342 |
|
* @return the prior mapping of the key, or null if there was none |
343 |
|
* @see #get(Object) |
344 |
|
* @see Object#equals(Object) |
345 |
*/ |
*/ |
346 |
public Object put(Object key, Object value) |
public Object put(Object key, Object value) |
347 |
{ |
{ |
348 |
modCount++; |
modCount++; |
349 |
int idx = hash(key); |
int idx = hash(key); |
350 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
351 |
|
|
352 |
while (e != null) |
while (e != null) |
353 |
{ |
{ |
354 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (key == null ? e.key == null : key.equals(e.key)) |
355 |
{ |
// Must use this method for necessary bookkeeping in LinkedHashMap. |
356 |
Object r = e.value; |
return e.setValue(value); |
357 |
e.value = value; |
else |
358 |
return r; |
e = e.next; |
|
} |
|
|
else |
|
|
{ |
|
|
e = e.next; |
|
|
} |
|
359 |
} |
} |
360 |
|
|
361 |
// At this point, we know we need to add a new entry. |
// At this point, we know we need to add a new entry. |
362 |
if (++size > threshold) |
if (++size > threshold) |
363 |
{ |
{ |
364 |
rehash(); |
rehash(); |
365 |
// Need a new hash value to suit the bigger table. |
// Need a new hash value to suit the bigger table. |
366 |
idx = hash(key); |
idx = hash(key); |
367 |
} |
} |
368 |
|
|
369 |
e = new Entry(key, value); |
// LinkedHashMap cannot override put(), hence this call. |
370 |
|
addEntry(key, value, idx); |
371 |
|
return null; |
372 |
|
} |
373 |
|
|
374 |
|
/** |
375 |
|
* Helper method for put, that creates and adds a new Entry. This is |
376 |
|
* overridden in LinkedHashMap for bookkeeping purposes. |
377 |
|
* |
378 |
|
* @param key the key of the new Entry |
379 |
|
* @param value the value |
380 |
|
* @param idx the index in buckets where the new Entry belongs |
381 |
|
* @see #put(Object, Object) |
382 |
|
*/ |
383 |
|
void addEntry(Object key, Object value, int idx) |
384 |
|
{ |
385 |
|
HashEntry e = new HashEntry(key, value); |
386 |
|
|
387 |
e.next = buckets[idx]; |
e.next = buckets[idx]; |
388 |
buckets[idx] = e; |
buckets[idx] = e; |
|
|
|
|
return null; |
|
389 |
} |
} |
390 |
|
|
391 |
/** |
/** |
392 |
* 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 |
393 |
* supplied key; if the key maps to nothing, then the HashMap remains unchanged, |
* supplied key. If the key maps to nothing, then the HashMap remains |
394 |
* and <pre>null</pre> is returned |
* unchanged, and <pre>null</pre> is returned. NOTE: Since the value |
395 |
|
* could also be null, you must use containsKey to see if you are |
396 |
|
* actually removing a mapping. |
397 |
* |
* |
398 |
* @param key the key used to locate the value to remove from the HashMap |
* @param key the key used to locate the value to remove |
399 |
|
* @return whatever the key mapped to, if present |
400 |
*/ |
*/ |
401 |
public Object remove(Object key) |
public Object remove(Object key) |
402 |
{ |
{ |
403 |
modCount++; |
modCount++; |
404 |
int idx = hash(key); |
int idx = hash(key); |
405 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
406 |
Entry last = null; |
HashEntry last = null; |
407 |
|
|
408 |
while (e != null) |
while (e != null) |
409 |
{ |
{ |
410 |
if (key == null ? e.key == null : key.equals(e.key)) |
if (key == null ? e.key == null : key.equals(e.key)) |
411 |
{ |
{ |
412 |
if (last == null) |
if (last == null) |
413 |
buckets[idx] = e.next; |
buckets[idx] = e.next; |
414 |
else |
else |
415 |
last.next = e.next; |
last.next = e.next; |
416 |
size--; |
size--; |
417 |
return e.value; |
// Method call necessary for LinkedHashMap to work correctly. |
418 |
} |
return e.cleanup(); |
419 |
last = e; |
} |
420 |
e = e.next; |
last = e; |
421 |
|
e = e.next; |
422 |
} |
} |
423 |
return null; |
return null; |
424 |
} |
} |
425 |
|
|
426 |
|
/** |
427 |
|
* Copies all elements of the given map into this hashtable. If this table |
428 |
|
* already has a mapping for a key, the new mapping replaces the current |
429 |
|
* one. |
430 |
|
* |
431 |
|
* @param m the map to be hashed into this |
432 |
|
*/ |
433 |
public void putAll(Map m) |
public void putAll(Map m) |
434 |
{ |
{ |
|
int msize = m.size(); |
|
435 |
Iterator itr = m.entrySet().iterator(); |
Iterator itr = m.entrySet().iterator(); |
436 |
|
|
437 |
for (int i=0; i < msize; i++) |
for (int msize = m.size(); msize > 0; msize--) |
438 |
{ |
{ |
439 |
Map.Entry e = (Map.Entry) itr.next(); |
Map.Entry e = (Map.Entry) itr.next(); |
440 |
// Optimize in case the Entry is one of our own. |
// Optimize in case the Entry is one of our own. |
441 |
if (e instanceof BasicMapEntry) |
if (e instanceof BasicMapEntry) |
442 |
{ |
{ |
443 |
BasicMapEntry entry = (BasicMapEntry) e; |
BasicMapEntry entry = (BasicMapEntry) e; |
444 |
put(entry.key, entry.value); |
put(entry.key, entry.value); |
445 |
} |
} |
446 |
else |
else |
447 |
{ |
{ |
448 |
put(e.getKey(), e.getValue()); |
put(e.getKey(), e.getValue()); |
449 |
} |
} |
450 |
} |
} |
451 |
} |
} |
452 |
|
|
453 |
|
/** |
454 |
|
* Clears the Map so it has no keys. This is O(1). |
455 |
|
*/ |
456 |
public void clear() |
public void clear() |
457 |
{ |
{ |
458 |
modCount++; |
modCount++; |
459 |
buckets = new Entry[buckets.length]; |
Arrays.fill(buckets, null); |
460 |
size = 0; |
size = 0; |
461 |
} |
} |
462 |
|
|
463 |
/** |
/** |
464 |
* returns a shallow clone of this HashMap (i.e. the Map itself is cloned, but |
* Returns a shallow clone of this HashMap. The Map itself is cloned, |
465 |
* its contents are not) |
* but its contents are not. This is O(n). |
466 |
|
* |
467 |
|
* @return the clone |
468 |
*/ |
*/ |
469 |
public Object clone() |
public Object clone() |
470 |
{ |
{ |
475 |
} |
} |
476 |
catch (CloneNotSupportedException x) |
catch (CloneNotSupportedException x) |
477 |
{ |
{ |
478 |
|
// This is impossible. |
479 |
} |
} |
480 |
copy.buckets = new Entry[buckets.length]; |
copy.buckets = new HashEntry[buckets.length]; |
481 |
|
|
482 |
for (int i=0; i < buckets.length; i++) |
for (int i = buckets.length - 1; i >= 0; i--) |
483 |
{ |
{ |
484 |
Entry e = buckets[i]; |
HashEntry e = buckets[i]; |
485 |
Entry last = null; |
HashEntry last = null; |
486 |
|
|
487 |
|
// Since LinkedHashMap does not override clone, we must clone |
488 |
|
// the HashEntries to get the one of the correct type. |
489 |
while (e != null) |
while (e != null) |
490 |
{ |
{ |
491 |
if (last == null) |
if (last == null) |
492 |
{ |
{ |
493 |
last = new Entry(e.key, e.value); |
last = (HashEntry) e.clone(); |
494 |
copy.buckets[i] = last; |
copy.buckets[i] = last; |
495 |
} |
} |
496 |
else |
else |
497 |
{ |
{ |
498 |
last.next = new Entry(e.key, e.value); |
last.next = (HashEntry) e.clone(); |
499 |
last = last.next; |
last = last.next; |
500 |
} |
} |
501 |
e = e.next; |
e = e.next; |
502 |
} |
} |
503 |
} |
} |
504 |
|
|
505 |
|
// Perform extra bookkeeping required by LinkedHashMap. |
506 |
|
if (this instanceof LinkedHashMap) |
507 |
|
((LinkedHashMap) this).rethread(); |
508 |
|
|
509 |
return copy; |
return copy; |
510 |
} |
} |
511 |
|
|
512 |
/** returns a "set view" of this HashMap's keys */ |
/** |
513 |
|
* Returns a "set view" of this HashMap's keys. The set is backed by the |
514 |
|
* HashMap, so changes in one show up in the other. The set supports |
515 |
|
* element removal, but not element addition. |
516 |
|
* |
517 |
|
* @return a set view of the keys |
518 |
|
* @see #values() |
519 |
|
* @see #entrySet() |
520 |
|
*/ |
521 |
public Set keySet() |
public Set keySet() |
522 |
{ |
{ |
523 |
// Create an AbstractSet with custom implementations of those methods that |
// Create an AbstractSet with custom implementations of those methods that |
524 |
// can be overriden easily and efficiently. |
// can be overridden easily and efficiently. |
525 |
return new AbstractSet() |
return new AbstractSet() |
526 |
{ |
{ |
527 |
public int size() |
public int size() |
528 |
{ |
{ |
529 |
return size; |
return size; |
530 |
} |
} |
531 |
|
|
532 |
public Iterator iterator() |
public Iterator iterator() |
533 |
{ |
{ |
534 |
return new HashIterator(HashIterator.KEYS); |
// Cannot create the iterator directly, because of LinkedHashMap. |
535 |
|
return HashMap.this.iterator(KEYS); |
536 |
} |
} |
537 |
|
|
538 |
public void clear() |
public void clear() |
539 |
{ |
{ |
540 |
HashMap.this.clear(); |
HashMap.this.clear(); |
544 |
{ |
{ |
545 |
return HashMap.this.containsKey(o); |
return HashMap.this.containsKey(o); |
546 |
} |
} |
547 |
|
|
548 |
public boolean remove(Object o) |
public boolean remove(Object o) |
549 |
{ |
{ |
550 |
// Test against the size of the HashMap to determine if anything |
// Test against the size of the HashMap to determine if anything |
551 |
// really got removed. This is neccessary because the return value of |
// really got removed. This is neccessary because the return value of |
552 |
// HashMap.remove() is ambiguous in the null case. |
// HashMap.remove() is ambiguous in the null case. |
553 |
int oldsize = size; |
int oldsize = size; |
554 |
HashMap.this.remove(o); |
HashMap.this.remove(o); |
555 |
return (oldsize != size); |
return (oldsize != size); |
556 |
} |
} |
557 |
}; |
}; |
558 |
} |
} |
559 |
|
|
560 |
/** Returns a "collection view" (or "bag view") of this HashMap's values. */ |
/** |
561 |
|
* Returns a "collection view" (or "bag view") of this HashMap's values. |
562 |
|
* The collection is backed by the HashMap, so changes in one show up |
563 |
|
* in the other. The collection supports element removal, but not element |
564 |
|
* addition. |
565 |
|
* |
566 |
|
* @return a bag view of the values |
567 |
|
* @see #keySet() |
568 |
|
* @see #entrySet() |
569 |
|
*/ |
570 |
public Collection values() |
public Collection values() |
571 |
{ |
{ |
572 |
// 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 |
577 |
{ |
{ |
578 |
return size; |
return size; |
579 |
} |
} |
580 |
|
|
581 |
public Iterator iterator() |
public Iterator iterator() |
582 |
{ |
{ |
583 |
return new HashIterator(HashIterator.VALUES); |
// Cannot create the iterator directly, because of LinkedHashMap. |
584 |
|
return HashMap.this.iterator(VALUES); |
585 |
} |
} |
586 |
|
|
587 |
public void clear() |
public void clear() |
588 |
{ |
{ |
589 |
HashMap.this.clear(); |
HashMap.this.clear(); |
591 |
}; |
}; |
592 |
} |
} |
593 |
|
|
594 |
/** Returns a "set view" of this HashMap's entries. */ |
/** |
595 |
|
* Returns a "set view" of this HashMap's entries. The set is backed by |
596 |
|
* the HashMap, so changes in one show up in the other. The set supports |
597 |
|
* element removal, but not element addition. |
598 |
|
* <p> |
599 |
|
* |
600 |
|
* Note that the iterators for all three views, from keySet(), entrySet(), |
601 |
|
* and values(), traverse the HashMap in the same sequence. |
602 |
|
* |
603 |
|
* @return a set view of the entries |
604 |
|
* @see #keySet() |
605 |
|
* @see #values() |
606 |
|
* @see Map.Entry |
607 |
|
*/ |
608 |
public Set entrySet() |
public Set entrySet() |
609 |
{ |
{ |
610 |
// Create an AbstractSet with custom implementations of those methods that |
// Create an AbstractSet with custom implementations of those methods that |
611 |
// can be overriden easily and efficiently. |
// can be overridden easily and efficiently. |
612 |
return new AbstractSet() |
return new AbstractSet() |
613 |
{ |
{ |
614 |
public int size() |
public int size() |
615 |
{ |
{ |
616 |
return size; |
return size; |
617 |
} |
} |
618 |
|
|
619 |
public Iterator iterator() |
public Iterator iterator() |
620 |
{ |
{ |
621 |
return new HashIterator(HashIterator.ENTRIES); |
// Cannot create the iterator directly, because of LinkedHashMap. |
622 |
|
return HashMap.this.iterator(ENTRIES); |
623 |
} |
} |
624 |
|
|
625 |
public void clear() |
public void clear() |
626 |
{ |
{ |
627 |
HashMap.this.clear(); |
HashMap.this.clear(); |
629 |
|
|
630 |
public boolean contains(Object o) |
public boolean contains(Object o) |
631 |
{ |
{ |
632 |
if (!(o instanceof Map.Entry)) |
return getEntry(o) != null; |
|
return false; |
|
|
Map.Entry me = (Map.Entry) o; |
|
|
Entry e = getEntry(me); |
|
|
return (e != null); |
|
633 |
} |
} |
634 |
|
|
635 |
public boolean remove(Object o) |
public boolean remove(Object o) |
636 |
{ |
{ |
637 |
if (!(o instanceof Map.Entry)) |
HashEntry e = getEntry(o); |
638 |
return false; |
if (e != null) |
639 |
Map.Entry me = (Map.Entry) o; |
{ |
640 |
Entry e = getEntry(me); |
HashMap.this.remove(e.key); |
641 |
if (e != null) |
return true; |
642 |
{ |
} |
643 |
HashMap.this.remove(e.key); |
return false; |
|
return true; |
|
|
} |
|
|
return false; |
|
644 |
} |
} |
645 |
}; |
}; |
646 |
} |
} |
|
|
|
|
/** Return an index in the buckets array for `key' based on its hashCode() */ |
|
|
private int hash(Object key) |
|
|
{ |
|
|
if (key == null) |
|
|
return 0; |
|
|
else |
|
|
return Math.abs(key.hashCode() % buckets.length); |
|
|
} |
|
647 |
|
|
648 |
/** Return an Entry who's key and value equal the supplied Map.Entry. |
/** Helper method that returns an index in the buckets array for `key; |
649 |
* This is used by entrySet's contains() and remove() methods. They can't |
* based on its hashCode(). |
650 |
* use contains(key) and remove(key) directly because that would result |
* |
651 |
* in entries with the same key but a different value being matched. */ |
* @param key the key |
652 |
private Entry getEntry(Map.Entry me) |
* @return the bucket number |
653 |
|
*/ |
654 |
|
int hash(Object key) |
655 |
{ |
{ |
656 |
|
return (key == null) ? 0 : Math.abs(key.hashCode() % buckets.length); |
657 |
|
} |
658 |
|
|
659 |
|
/** |
660 |
|
* Helper method for entrySet(), which matches both key and value |
661 |
|
* simultaneously. |
662 |
|
* |
663 |
|
* @param o the entry to match |
664 |
|
* @return the matching entry, if found, or null |
665 |
|
* @see #entrySet() |
666 |
|
*/ |
667 |
|
private HashEntry getEntry(Object o) |
668 |
|
{ |
669 |
|
if (!(o instanceof Map.Entry)) |
670 |
|
return null; |
671 |
|
Map.Entry me = (Map.Entry) o; |
672 |
int idx = hash(me.getKey()); |
int idx = hash(me.getKey()); |
673 |
Entry e = buckets[idx]; |
HashEntry e = buckets[idx]; |
674 |
while (e != null) |
while (e != null) |
675 |
{ |
{ |
676 |
if (e.equals(me)) |
if (e.equals(me)) |
677 |
return e; |
return e; |
678 |
e = e.next; |
e = e.next; |
679 |
} |
} |
680 |
return null; |
return null; |
681 |
} |
} |
682 |
|
|
683 |
/** |
/** |
684 |
* 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 |
685 |
* 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 |
686 |
* size() > threshold. Note that the existing Entry objects are reused in |
* size() > threshold. Note that the existing Entry objects are reused in |
687 |
* the new hash table. |
* the new hash table. |
688 |
|
* <p> |
689 |
|
* |
690 |
|
* This is not specified, but the new size is twice the current size plus |
691 |
|
* one; this number is not always prime, unfortunately. |
692 |
*/ |
*/ |
693 |
private void rehash() |
private void rehash() |
694 |
{ |
{ |
695 |
Entry[] oldBuckets = buckets; |
HashEntry[] oldBuckets = buckets; |
696 |
|
|
697 |
int newcapacity = (buckets.length * 2) + 1; |
int newcapacity = (buckets.length * 2) + 1; |
698 |
threshold = (int) (newcapacity * loadFactor); |
threshold = (int) (newcapacity * loadFactor); |
699 |
buckets = new Entry[newcapacity]; |
buckets = new HashEntry[newcapacity]; |
700 |
|
|
701 |
for (int i = 0; i < oldBuckets.length; i++) |
for (int i = oldBuckets.length - 1; i >= 0; i--) |
702 |
{ |
{ |
703 |
Entry e = oldBuckets[i]; |
HashEntry e = oldBuckets[i]; |
704 |
while (e != null) |
while (e != null) |
705 |
{ |
{ |
706 |
int idx = hash(e.key); |
int idx = hash(e.key); |
707 |
Entry dest = buckets[idx]; |
HashEntry dest = buckets[idx]; |
708 |
|
|
709 |
if (dest != null) |
if (dest != null) |
710 |
{ |
{ |
711 |
while (dest.next != null) |
while (dest.next != null) |
712 |
dest = dest.next; |
dest = dest.next; |
713 |
dest.next = e; |
dest.next = e; |
714 |
} |
} |
715 |
else |
else |
716 |
{ |
{ |
717 |
buckets[idx] = e; |
buckets[idx] = e; |
718 |
} |
} |
719 |
|
|
720 |
Entry next = e.next; |
HashEntry next = e.next; |
721 |
e.next = null; |
e.next = null; |
722 |
e = next; |
e = next; |
723 |
} |
} |
724 |
} |
} |
725 |
} |
} |
726 |
|
|
727 |
/** |
/** |
728 |
|
* Generates a parameterized iterator. Must be overrideable, since |
729 |
|
* LinkedHashMap iterates in a different order. |
730 |
|
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} |
731 |
|
* @return the appropriate iterator |
732 |
|
*/ |
733 |
|
Iterator iterator(int type) |
734 |
|
{ |
735 |
|
return new HashIterator(type); |
736 |
|
} |
737 |
|
|
738 |
|
/** |
739 |
* Serializes this object to the given stream. |
* Serializes this object to the given stream. |
740 |
* @serialdata the <i>capacity</i>(int) that is the length of the |
* |
741 |
* bucket array, the <i>size</i>(int) of the hash map are emitted |
* @param s the stream to write to |
742 |
* first. They are followed by size entries, each consisting of |
* @throws IOException if the underlying stream fails |
743 |
* a key (Object) and a value (Object). |
* @serialData the <i>capacity</i>(int) that is the length of the |
744 |
|
* bucket array, the <i>size</i>(int) of the hash map |
745 |
|
* are emitted first. They are followed by size entries, |
746 |
|
* each consisting of a key (Object) and a value (Object). |
747 |
*/ |
*/ |
748 |
private void writeObject(ObjectOutputStream s) throws IOException |
private void writeObject(ObjectOutputStream s) throws IOException |
749 |
{ |
{ |
750 |
// the threshold and loadFactor fields |
// Write the threshold and loadFactor fields. |
751 |
s.defaultWriteObject(); |
s.defaultWriteObject(); |
752 |
|
|
753 |
s.writeInt(buckets.length); |
s.writeInt(buckets.length); |
754 |
s.writeInt(size); |
s.writeInt(size); |
755 |
Iterator it = entrySet().iterator(); |
// Avoid creating a wasted Set by creating the iterator directly. |
756 |
|
Iterator it = iterator(ENTRIES); |
757 |
while (it.hasNext()) |
while (it.hasNext()) |
758 |
{ |
{ |
759 |
Map.Entry entry = (Map.Entry) it.next(); |
HashEntry entry = (HashEntry) it.next(); |
760 |
s.writeObject(entry.getKey()); |
s.writeObject(entry.key); |
761 |
s.writeObject(entry.getValue()); |
s.writeObject(entry.value); |
762 |
} |
} |
763 |
} |
} |
764 |
|
|
765 |
/** |
/** |
766 |
* Deserializes this object from the given stream. |
* Deserializes this object from the given stream. |
767 |
* @serialdata the <i>capacity</i>(int) that is the length of the |
* |
768 |
* bucket array, the <i>size</i>(int) of the hash map are emitted |
* @param s the stream to read from |
769 |
* first. They are followed by size entries, each consisting of |
* @throws ClassNotFoundException if the underlying stream fails |
770 |
* a key (Object) and a value (Object). |
* @throws IOException if the underlying stream fails |
771 |
|
* @serialData the <i>capacity</i>(int) that is the length of the |
772 |
|
* bucket array, the <i>size</i>(int) of the hash map |
773 |
|
* are emitted first. They are followed by size entries, |
774 |
|
* each consisting of a key (Object) and a value (Object). |
775 |
*/ |
*/ |
776 |
private void readObject(ObjectInputStream s) |
private void readObject(ObjectInputStream s) |
777 |
throws IOException, ClassNotFoundException |
throws IOException, ClassNotFoundException |
778 |
{ |
{ |
779 |
// the threshold and loadFactor fields |
// Read the threshold and loadFactor fields. |
780 |
s.defaultReadObject(); |
s.defaultReadObject(); |
781 |
|
|
782 |
int capacity = s.readInt(); |
// Read and use capacity. |
783 |
|
buckets = new HashEntry[s.readInt()]; |
784 |
int len = s.readInt(); |
int len = s.readInt(); |
785 |
size = 0; |
// Already happens automatically. |
786 |
modCount = 0; |
// size = 0; |
787 |
buckets = new Entry[capacity]; |
// modCount = 0; |
788 |
|
|
789 |
for (int i = 0; i < len; i++) |
// Read and use key/value pairs. |
790 |
{ |
for ( ; len > 0; len--) |
791 |
Object key = s.readObject(); |
put(s.readObject(), s.readObject()); |
|
Object value = s.readObject(); |
|
|
put(key, value); |
|
|
} |
|
792 |
} |
} |
793 |
|
|
794 |
/** |
/** |
796 |
* This implementation is parameterized to give a sequential view of |
* This implementation is parameterized to give a sequential view of |
797 |
* keys, values, or entries. |
* keys, values, or entries. |
798 |
* |
* |
799 |
* @author Jon Zeppieri |
* @author Jon Zeppieri |
800 |
*/ |
*/ |
801 |
class HashIterator implements Iterator |
class HashIterator implements Iterator |
802 |
{ |
{ |
803 |
static final int KEYS = 0, |
/** |
804 |
VALUES = 1, |
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, |
805 |
ENTRIES = 2; |
* or {@link #ENTRIES}. |
806 |
|
*/ |
807 |
// the type of this Iterator: KEYS, VALUES, or ENTRIES. |
final int type; |
808 |
int type; |
/** |
809 |
// the number of modifications to the backing Hashtable that we know about. |
* The number of modifications to the backing HashMap that we know about. |
810 |
int knownMod; |
*/ |
811 |
// The total number of elements returned by next(). Used to determine if |
int knownMod = modCount; |
812 |
// there are more elements remaining. |
/** The number of elements remaining to be returned by next(). */ |
813 |
int count; |
int count = size; |
814 |
// Current index in the physical hash table. |
/** Current index in the physical hash table. */ |
815 |
int idx; |
int idx = buckets.length; |
816 |
// The last Entry returned by a next() call. |
/** The last Entry returned by a next() call. */ |
817 |
Entry last; |
HashEntry last; |
818 |
// The next entry that should be returned by next(). It is set to something |
/** |
819 |
// 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 |
820 |
// entries. It is null if next() needs to find a new bucket. |
* if we're iterating through a bucket that contains multiple linked |
821 |
Entry next; |
* entries. It is null if next() needs to find a new bucket. |
822 |
|
*/ |
823 |
|
HashEntry next; |
824 |
|
|
825 |
/* construct a new HashtableIterator with the supplied type: |
/** |
826 |
KEYS, VALUES, or ENTRIES */ |
* Construct a new HashIterator with the supplied type. |
827 |
|
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} |
828 |
|
*/ |
829 |
HashIterator(int type) |
HashIterator(int type) |
830 |
{ |
{ |
831 |
this.type = type; |
this.type = type; |
|
knownMod = HashMap.this.modCount; |
|
|
count = 0; |
|
|
idx = buckets.length; |
|
832 |
} |
} |
833 |
|
|
834 |
/** returns true if the Iterator has more elements */ |
/** |
835 |
|
* Returns true if the Iterator has more elements. |
836 |
|
* @return true if there are more elements |
837 |
|
* @throws ConcurrentModificationException if the HashMap was modified |
838 |
|
*/ |
839 |
public boolean hasNext() |
public boolean hasNext() |
840 |
{ |
{ |
841 |
if (knownMod != HashMap.this.modCount) |
if (knownMod != modCount) |
842 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
843 |
return count < size; |
return count > 0; |
844 |
} |
} |
845 |
|
|
846 |
/** returns the next element in the Iterator's sequential view */ |
/** |
847 |
|
* Returns the next element in the Iterator's sequential view. |
848 |
|
* @return the next element |
849 |
|
* @throws ConcurrentModificationException if the HashMap was modified |
850 |
|
* @throws NoSuchElementException if there is none |
851 |
|
*/ |
852 |
public Object next() |
public Object next() |
853 |
{ |
{ |
854 |
if (knownMod != HashMap.this.modCount) |
if (knownMod != modCount) |
855 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
856 |
if (count == size) |
if (count == 0) |
857 |
throw new NoSuchElementException(); |
throw new NoSuchElementException(); |
858 |
count++; |
count--; |
859 |
Entry e = null; |
HashEntry e = next; |
|
if (next != null) |
|
|
e = next; |
|
860 |
|
|
861 |
while (e == null) |
while (e == null) |
862 |
{ |
e = buckets[--idx]; |
|
e = buckets[--idx]; |
|
|
} |
|
863 |
|
|
864 |
next = e.next; |
next = e.next; |
865 |
last = e; |
last = e; |
870 |
return e; |
return e; |
871 |
} |
} |
872 |
|
|
873 |
/** |
/** |
874 |
* removes from the backing HashMap the last element which was fetched with the |
* Removes from the backing HashMap the last element which was fetched |
875 |
* <pre>next()</pre> method |
* with the <pre>next()</pre> method. |
876 |
|
* @throws ConcurrentModificationException if the HashMap was modified |
877 |
|
* @throws IllegalStateException if called when there is no last element |
878 |
*/ |
*/ |
879 |
public void remove() |
public void remove() |
880 |
{ |
{ |
881 |
if (knownMod != HashMap.this.modCount) |
if (knownMod != modCount) |
882 |
throw new ConcurrentModificationException(); |
throw new ConcurrentModificationException(); |
883 |
if (last == null) |
if (last == null) |
884 |
{ |
throw new IllegalStateException(); |
885 |
throw new IllegalStateException(); |
|
886 |
} |
HashMap.this.remove(last.key); |
887 |
else |
knownMod++; |
888 |
{ |
last = null; |
|
HashMap.this.remove(last.key); |
|
|
knownMod++; |
|
|
count--; |
|
|
last = null; |
|
|
} |
|
889 |
} |
} |
890 |
} |
} |
891 |
} |
} |