5 |
|
*/ |
6 |
|
|
7 |
|
package java.util.concurrent; |
8 |
< |
|
8 |
> |
import java.util.concurrent.locks.*; |
9 |
|
import java.util.*; |
10 |
|
import java.io.Serializable; |
11 |
|
import java.io.IOException; |
22 |
|
* entire table in a way that prevents all access. This class is |
23 |
|
* fully interoperable with Hashtable in programs that rely on its |
24 |
|
* thread safety but not on its synchronization details. |
25 |
< |
* |
25 |
> |
* |
26 |
|
* <p> Retrieval operations (including <tt>get</tt>) ordinarily |
27 |
|
* overlap with update operations (including <tt>put</tt> and |
28 |
|
* <tt>remove</tt>). Retrievals reflect the results of the most |
62 |
|
*/ |
63 |
|
|
64 |
|
/* ---------------- Constants -------------- */ |
65 |
< |
|
65 |
> |
|
66 |
|
/** |
67 |
|
* The default initial number of table slots for this table (32). |
68 |
|
* Used when not otherwise specified in constructor. |
69 |
|
*/ |
70 |
< |
private static int DEFAULT_INITIAL_CAPACITY = 16; |
70 |
> |
private static int DEFAULT_INITIAL_CAPACITY = 16; |
71 |
|
|
72 |
|
/** |
73 |
|
* The maximum capacity, used if a higher value is implicitly |
75 |
|
* be a power of two <= 1<<30. |
76 |
|
*/ |
77 |
|
static final int MAXIMUM_CAPACITY = 1 << 30; |
78 |
< |
|
78 |
> |
|
79 |
|
/** |
80 |
|
* The default load factor for this table. Used when not |
81 |
|
* otherwise specified in constructor. |
82 |
|
*/ |
83 |
< |
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
83 |
> |
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
84 |
|
|
85 |
|
/** |
86 |
|
* The default number of concurrency control segments. |
90 |
|
/* ---------------- Fields -------------- */ |
91 |
|
|
92 |
|
/** |
93 |
< |
* Mask value for indexing into segments. The lower bits of a |
94 |
< |
* key's hash code are used to choose the segment, and the |
95 |
< |
* remaining bits are used as the placement hashcode used within |
96 |
< |
* the segment. |
93 |
> |
* Mask value for indexing into segments. The upper bits of a |
94 |
> |
* key's hash code are used to choose the segment. |
95 |
|
**/ |
96 |
|
private final int segmentMask; |
97 |
|
|
103 |
|
/** |
104 |
|
* The segments, each of which is a specialized hash table |
105 |
|
*/ |
106 |
< |
private final Segment<K,V>[] segments; |
106 |
> |
private final Segment[] segments; |
107 |
|
|
108 |
|
private transient Set<K> keySet; |
109 |
< |
private transient Set/*<Map.Entry<K,V>>*/ entrySet; |
109 |
> |
private transient Set<Map.Entry<K,V>> entrySet; |
110 |
|
private transient Collection<V> values; |
111 |
|
|
112 |
|
/* ---------------- Small Utilities -------------- */ |
113 |
|
|
114 |
|
/** |
115 |
< |
* Return a hash code for non-null Object x. |
115 |
> |
* Return a hash code for non-null Object x. |
116 |
|
* Uses the same hash code spreader as most other j.u hash tables. |
117 |
|
* @param x the object serving as a key |
118 |
|
* @return the hash code |
126 |
|
return h; |
127 |
|
} |
128 |
|
|
131 |
– |
/** |
132 |
– |
* Check for equality of non-null references x and y. |
133 |
– |
**/ |
134 |
– |
private static boolean eq(Object x, Object y) { |
135 |
– |
return x == y || x.equals(y); |
136 |
– |
} |
137 |
– |
|
138 |
– |
/** |
139 |
– |
* Return index for hash code h in table of given length. |
140 |
– |
*/ |
141 |
– |
private static int indexFor(int h, int length) { |
142 |
– |
return h & (length-1); |
143 |
– |
} |
144 |
– |
|
129 |
|
/** |
130 |
|
* Return the segment that should be used for key with given hash |
131 |
|
*/ |
132 |
|
private Segment<K,V> segmentFor(int hash) { |
133 |
< |
return segments[hash & segmentMask]; |
150 |
< |
} |
151 |
< |
|
152 |
< |
/** |
153 |
< |
* Strip the segment index from hash code to use as a per-segment hash. |
154 |
< |
*/ |
155 |
< |
private int segmentHashFor(int hash) { |
156 |
< |
return hash >>> segmentShift; |
133 |
> |
return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask]; |
134 |
|
} |
135 |
|
|
136 |
|
/* ---------------- Inner Classes -------------- */ |
170 |
|
* - All unsynchronized read operations must first read the |
171 |
|
* "count" field, and should not look at table entries if |
172 |
|
* it is 0. |
173 |
< |
* |
173 |
> |
* |
174 |
|
* - All synchronized write operations should write to |
175 |
|
* the "count" field after updating. The operations must not |
176 |
|
* take any action that could even momentarily cause |
184 |
|
* As a guide, all critical volatile reads and writes are marked |
185 |
|
* in code comments. |
186 |
|
*/ |
187 |
< |
|
187 |
> |
|
188 |
|
/** |
189 |
|
* The number of elements in this segment's region. |
190 |
|
**/ |
200 |
|
/** |
201 |
|
* The per-segment table |
202 |
|
*/ |
203 |
< |
transient HashEntry<K,V>[] table; |
203 |
> |
transient HashEntry[] table; |
204 |
|
|
205 |
|
/** |
206 |
|
* The load factor for the hash table. Even though this value |
212 |
|
|
213 |
|
Segment(int initialCapacity, float lf) { |
214 |
|
loadFactor = lf; |
215 |
< |
setTable(new HashEntry<K,V>[initialCapacity]); |
215 |
> |
setTable(new HashEntry[initialCapacity]); |
216 |
|
} |
217 |
|
|
218 |
|
/** |
219 |
< |
* Set table to new HashEntry array. |
219 |
> |
* Set table to new HashEntry array. |
220 |
|
* Call only while holding lock or in constructor. |
221 |
|
**/ |
222 |
< |
private void setTable(HashEntry<K,V>[] newTable) { |
222 |
> |
private void setTable(HashEntry[] newTable) { |
223 |
|
table = newTable; |
224 |
|
threshold = (int)(newTable.length * loadFactor); |
225 |
|
count = count; // write-volatile |
226 |
< |
} |
226 |
> |
} |
227 |
|
|
228 |
|
/* Specialized implementations of map methods */ |
229 |
< |
|
230 |
< |
V get(K key, int hash) { |
229 |
> |
|
230 |
> |
V get(K key, int hash) { |
231 |
|
if (count != 0) { // read-volatile |
232 |
< |
HashEntry<K,V>[] tab = table; |
233 |
< |
int index = indexFor(hash, tab.length); |
234 |
< |
HashEntry<K,V> e = tab[index]; |
232 |
> |
HashEntry[] tab = table; |
233 |
> |
int index = hash & (tab.length - 1); |
234 |
> |
HashEntry<K,V> e = (HashEntry<K,V>) tab[index]; |
235 |
|
while (e != null) { |
236 |
< |
if (e.hash == hash && eq(key, e.key)) |
236 |
> |
if (e.hash == hash && key.equals(e.key)) |
237 |
|
return e.value; |
238 |
|
e = e.next; |
239 |
|
} |
243 |
|
|
244 |
|
boolean containsKey(Object key, int hash) { |
245 |
|
if (count != 0) { // read-volatile |
246 |
< |
HashEntry<K,V>[] tab = table; |
247 |
< |
int index = indexFor(hash, tab.length); |
248 |
< |
HashEntry<K,V> e = tab[index]; |
246 |
> |
HashEntry[] tab = table; |
247 |
> |
int index = hash & (tab.length - 1); |
248 |
> |
HashEntry<K,V> e = (HashEntry<K,V>) tab[index]; |
249 |
|
while (e != null) { |
250 |
< |
if (e.hash == hash && eq(key, e.key)) |
250 |
> |
if (e.hash == hash && key.equals(e.key)) |
251 |
|
return true; |
252 |
|
e = e.next; |
253 |
|
} |
254 |
|
} |
255 |
|
return false; |
256 |
|
} |
257 |
< |
|
257 |
> |
|
258 |
|
boolean containsValue(Object value) { |
259 |
|
if (count != 0) { // read-volatile |
260 |
< |
HashEntry<K,V> tab[] = table; |
260 |
> |
HashEntry[] tab = table; |
261 |
|
int len = tab.length; |
262 |
< |
for (int i = 0 ; i < len; i++) |
263 |
< |
for (HashEntry<K,V> e = tab[i] ; e != null ; e = e.next) |
262 |
> |
for (int i = 0 ; i < len; i++) |
263 |
> |
for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i] ; e != null ; e = e.next) |
264 |
|
if (value.equals(e.value)) |
265 |
|
return true; |
266 |
|
} |
267 |
|
return false; |
268 |
|
} |
269 |
|
|
270 |
< |
V put(K key, int hash, V value, boolean onlyIfAbsent) { |
270 |
> |
V put(K key, int hash, V value, boolean onlyIfAbsent) { |
271 |
|
lock(); |
272 |
|
try { |
273 |
< |
HashEntry<K,V>[] tab = table; |
274 |
< |
int index = indexFor(hash, tab.length); |
275 |
< |
HashEntry<K,V> first = tab[index]; |
276 |
< |
|
277 |
< |
for (HashEntry<K,V> e = first; e != null; e = e.next) { |
278 |
< |
if (e.hash == hash && eq(key, e.key)) { |
279 |
< |
V oldValue = e.value; |
273 |
> |
int c = count; |
274 |
> |
HashEntry[] tab = table; |
275 |
> |
int index = hash & (tab.length - 1); |
276 |
> |
HashEntry<K,V> first = (HashEntry<K,V>) tab[index]; |
277 |
> |
|
278 |
> |
for (HashEntry<K,V> e = first; e != null; e = (HashEntry<K,V>) e.next) { |
279 |
> |
if (e.hash == hash && key.equals(e.key)) { |
280 |
> |
V oldValue = e.value; |
281 |
|
if (!onlyIfAbsent) |
282 |
|
e.value = value; |
283 |
< |
count = count; // write-volatile |
283 |
> |
count = c; // write-volatile |
284 |
|
return oldValue; |
285 |
|
} |
286 |
|
} |
287 |
< |
|
287 |
> |
|
288 |
|
tab[index] = new HashEntry<K,V>(hash, key, value, first); |
289 |
< |
if (++count > threshold) // write-volatile |
290 |
< |
rehash(); |
289 |
> |
++c; |
290 |
> |
count = c; // write-volatile |
291 |
> |
if (c > threshold) |
292 |
> |
setTable(rehash(tab)); |
293 |
|
return null; |
294 |
|
} |
295 |
|
finally { |
297 |
|
} |
298 |
|
} |
299 |
|
|
300 |
< |
private void rehash() { |
321 |
< |
HashEntry<K,V>[] oldTable = table; |
300 |
> |
private HashEntry[] rehash(HashEntry[] oldTable) { |
301 |
|
int oldCapacity = oldTable.length; |
302 |
|
if (oldCapacity >= MAXIMUM_CAPACITY) |
303 |
< |
return; |
303 |
> |
return oldTable; |
304 |
|
|
305 |
|
/* |
306 |
|
* Reclassify nodes in each list to new Map. Because we are |
315 |
|
* reader thread that may be in the midst of traversing table |
316 |
|
* right now. |
317 |
|
*/ |
318 |
< |
|
319 |
< |
HashEntry<K,V>[] newTable = new HashEntry<K,V>[oldCapacity << 1]; |
318 |
> |
|
319 |
> |
HashEntry[] newTable = new HashEntry[oldCapacity << 1]; |
320 |
|
int sizeMask = newTable.length - 1; |
321 |
|
for (int i = 0; i < oldCapacity ; i++) { |
322 |
|
// We need to guarantee that any existing reads of old Map can |
323 |
< |
// proceed. So we cannot yet null out each bin. |
324 |
< |
HashEntry<K,V> e = oldTable[i]; |
325 |
< |
|
323 |
> |
// proceed. So we cannot yet null out each bin. |
324 |
> |
HashEntry<K,V> e = (HashEntry<K,V>)oldTable[i]; |
325 |
> |
|
326 |
|
if (e != null) { |
327 |
|
HashEntry<K,V> next = e.next; |
328 |
|
int idx = e.hash & sizeMask; |
329 |
< |
|
329 |
> |
|
330 |
|
// Single node on list |
331 |
< |
if (next == null) |
331 |
> |
if (next == null) |
332 |
|
newTable[idx] = e; |
333 |
< |
|
334 |
< |
else { |
333 |
> |
|
334 |
> |
else { |
335 |
|
// Reuse trailing consecutive sequence at same slot |
336 |
|
HashEntry<K,V> lastRun = e; |
337 |
|
int lastIdx = idx; |
338 |
< |
for (HashEntry<K,V> last = next; |
339 |
< |
last != null; |
338 |
> |
for (HashEntry<K,V> last = next; |
339 |
> |
last != null; |
340 |
|
last = last.next) { |
341 |
|
int k = last.hash & sizeMask; |
342 |
|
if (k != lastIdx) { |
345 |
|
} |
346 |
|
} |
347 |
|
newTable[lastIdx] = lastRun; |
348 |
< |
|
348 |
> |
|
349 |
|
// Clone all remaining nodes |
350 |
|
for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { |
351 |
|
int k = p.hash & sizeMask; |
352 |
< |
newTable[k] = new HashEntry<K,V>(p.hash, |
353 |
< |
p.key, |
354 |
< |
p.value, |
355 |
< |
newTable[k]); |
352 |
> |
newTable[k] = new HashEntry<K,V>(p.hash, |
353 |
> |
p.key, |
354 |
> |
p.value, |
355 |
> |
(HashEntry<K,V>) newTable[k]); |
356 |
|
} |
357 |
|
} |
358 |
|
} |
359 |
|
} |
360 |
< |
setTable(newTable); |
360 |
> |
return newTable; |
361 |
|
} |
362 |
|
|
363 |
|
/** |
364 |
|
* Remove; match on key only if value null, else match both. |
365 |
|
*/ |
366 |
|
V remove(Object key, int hash, Object value) { |
367 |
< |
lock(); |
367 |
> |
lock(); |
368 |
|
try { |
369 |
+ |
int c = count; |
370 |
|
HashEntry[] tab = table; |
371 |
< |
int index = indexFor(hash, tab.length); |
372 |
< |
HashEntry<K,V> first = tab[index]; |
373 |
< |
|
371 |
> |
int index = hash & (tab.length - 1); |
372 |
> |
HashEntry<K,V> first = (HashEntry<K,V>)tab[index]; |
373 |
> |
|
374 |
|
HashEntry<K,V> e = first; |
375 |
< |
while (true) { |
375 |
> |
for (;;) { |
376 |
|
if (e == null) |
377 |
|
return null; |
378 |
< |
if (e.hash == hash && eq(key, e.key)) |
378 |
> |
if (e.hash == hash && key.equals(e.key)) |
379 |
|
break; |
380 |
|
e = e.next; |
381 |
|
} |
383 |
|
V oldValue = e.value; |
384 |
|
if (value != null && !value.equals(oldValue)) |
385 |
|
return null; |
386 |
< |
|
386 |
> |
|
387 |
|
// All entries following removed node can stay in list, but |
388 |
< |
// all preceeding ones need to be cloned. |
388 |
> |
// all preceeding ones need to be cloned. |
389 |
|
HashEntry<K,V> newFirst = e.next; |
390 |
< |
for (HashEntry<K,V> p = first; p != e; p = p.next) |
391 |
< |
newFirst = new HashEntry<K,V>(p.hash, p.key, |
390 |
> |
for (HashEntry<K,V> p = first; p != e; p = p.next) |
391 |
> |
newFirst = new HashEntry<K,V>(p.hash, p.key, |
392 |
|
p.value, newFirst); |
393 |
|
tab[index] = newFirst; |
394 |
< |
|
395 |
< |
count--; // write-volatile |
416 |
< |
return e.value; |
394 |
> |
count = c-1; // write-volatile |
395 |
> |
return oldValue; |
396 |
|
} |
397 |
|
finally { |
398 |
|
unlock(); |
402 |
|
void clear() { |
403 |
|
lock(); |
404 |
|
try { |
405 |
< |
HashEntry<K,V> tab[] = table; |
406 |
< |
for (int i = 0; i < tab.length ; i++) |
405 |
> |
HashEntry[] tab = table; |
406 |
> |
for (int i = 0; i < tab.length ; i++) |
407 |
|
tab[i] = null; |
408 |
|
count = 0; // write-volatile |
409 |
|
} |
434 |
|
} |
435 |
|
|
436 |
|
public V getValue() { |
437 |
< |
return value; |
437 |
> |
return value; |
438 |
|
} |
439 |
|
|
440 |
|
public V setValue(V newValue) { |
450 |
|
public boolean equals(Object o) { |
451 |
|
if (!(o instanceof Entry)) |
452 |
|
return false; |
453 |
< |
Entry<K,V> e = (Entry)o; |
453 |
> |
Entry<K,V> e = (Entry<K,V>)o; |
454 |
|
return (key.equals(e.getKey()) && value.equals(e.getValue())); |
455 |
|
} |
456 |
< |
|
456 |
> |
|
457 |
|
public int hashCode() { |
458 |
|
return key.hashCode() ^ value.hashCode(); |
459 |
|
} |
463 |
|
} |
464 |
|
} |
465 |
|
|
466 |
< |
|
466 |
> |
|
467 |
|
/* ---------------- Public operations -------------- */ |
468 |
|
|
469 |
|
/** |
479 |
|
* negative or the load factor or number of segments are |
480 |
|
* nonpositive. |
481 |
|
*/ |
482 |
< |
public ConcurrentHashMap(int initialCapacity, |
482 |
> |
public ConcurrentHashMap(int initialCapacity, |
483 |
|
float loadFactor, int segments) { |
484 |
|
if (!(loadFactor > 0) || initialCapacity < 0 || segments <= 0) |
485 |
|
throw new IllegalArgumentException(); |
491 |
|
++sshift; |
492 |
|
ssize <<= 1; |
493 |
|
} |
494 |
< |
segmentShift = sshift; |
494 |
> |
segmentShift = 32 - sshift; |
495 |
|
segmentMask = ssize - 1; |
496 |
< |
this.segments = new Segment<K,V>[ssize]; |
496 |
> |
this.segments = new Segment[ssize]; |
497 |
|
|
498 |
|
if (initialCapacity > MAXIMUM_CAPACITY) |
499 |
|
initialCapacity = MAXIMUM_CAPACITY; |
500 |
|
int c = initialCapacity / ssize; |
501 |
< |
if (c * ssize < initialCapacity) |
501 |
> |
if (c * ssize < initialCapacity) |
502 |
|
++c; |
503 |
|
int cap = 1; |
504 |
|
while (cap < c) |
523 |
|
|
524 |
|
/** |
525 |
|
* Constructs a new, empty map with a default initial capacity, |
526 |
< |
* load factor, and number of segments |
526 |
> |
* load factor, and number of segments. |
527 |
|
*/ |
528 |
|
public ConcurrentHashMap() { |
529 |
|
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); |
568 |
|
* <code>null</code>. |
569 |
|
* @see #put(Object, Object) |
570 |
|
*/ |
571 |
< |
public V get(K key) { |
571 |
> |
public V get(Object key) { |
572 |
|
int hash = hash(key); // throws NullPointerException if key null |
573 |
< |
return segmentFor(hash).get(key, segmentHashFor(hash)); |
573 |
> |
return segmentFor(hash).get((K) key, hash); |
574 |
|
} |
575 |
|
|
576 |
|
/** |
577 |
|
* Tests if the specified object is a key in this table. |
578 |
< |
* |
578 |
> |
* |
579 |
|
* @param key possible key. |
580 |
< |
* @return <code>true</code> if and only if the specified object |
581 |
< |
* is a key in this table, as determined by the |
580 |
> |
* @return <code>true</code> if and only if the specified object |
581 |
> |
* is a key in this table, as determined by the |
582 |
|
* <tt>equals</tt> method; <code>false</code> otherwise. |
583 |
|
* @throws NullPointerException if the key is |
584 |
|
* <code>null</code>. |
586 |
|
*/ |
587 |
|
public boolean containsKey(Object key) { |
588 |
|
int hash = hash(key); // throws NullPointerException if key null |
589 |
< |
return segmentFor(hash).containsKey(key, segmentHashFor(hash)); |
589 |
> |
return segmentFor(hash).containsKey(key, hash); |
590 |
|
} |
591 |
|
|
592 |
|
/** |
597 |
|
* |
598 |
|
* @param value value whose presence in this map is to be tested. |
599 |
|
* @return <tt>true</tt> if this map maps one or more keys to the |
600 |
< |
* specified value. |
600 |
> |
* specified value. |
601 |
|
* @throws NullPointerException if the value is <code>null</code>. |
602 |
|
*/ |
603 |
|
public boolean containsValue(Object value) { |
604 |
< |
if (value == null) |
604 |
> |
if (value == null) |
605 |
|
throw new NullPointerException(); |
606 |
|
|
607 |
|
for (int i = 0; i < segments.length; ++i) { |
617 |
|
* |
618 |
|
* Note that this method is identical in functionality to containsValue, |
619 |
|
* (which is part of the Map interface in the collections framework). |
620 |
< |
* |
620 |
> |
* |
621 |
|
* @param value a value to search for. |
622 |
|
* @return <code>true</code> if and only if some key maps to the |
623 |
< |
* <code>value</code> argument in this table as |
623 |
> |
* <code>value</code> argument in this table as |
624 |
|
* determined by the <tt>equals</tt> method; |
625 |
|
* <code>false</code> otherwise. |
626 |
|
* @throws NullPointerException if the value is <code>null</code>. |
633 |
|
} |
634 |
|
|
635 |
|
/** |
636 |
< |
* Maps the specified <code>key</code> to the specified |
637 |
< |
* <code>value</code> in this table. Neither the key nor the |
636 |
> |
* Maps the specified <code>key</code> to the specified |
637 |
> |
* <code>value</code> in this table. Neither the key nor the |
638 |
|
* value can be <code>null</code>. <p> |
639 |
|
* |
640 |
< |
* The value can be retrieved by calling the <code>get</code> method |
641 |
< |
* with a key that is equal to the original key. |
640 |
> |
* The value can be retrieved by calling the <code>get</code> method |
641 |
> |
* with a key that is equal to the original key. |
642 |
|
* |
643 |
|
* @param key the table key. |
644 |
|
* @param value the value. |
649 |
|
* @see Object#equals(Object) |
650 |
|
* @see #get(Object) |
651 |
|
*/ |
652 |
< |
public V put(K key, V value) { |
653 |
< |
if (value == null) |
652 |
> |
public V put(K key, V value) { |
653 |
> |
if (value == null) |
654 |
|
throw new NullPointerException(); |
655 |
< |
int hash = hash(key); |
656 |
< |
return segmentFor(hash).put(key, segmentHashFor(hash), value, false); |
655 |
> |
int hash = hash(key); |
656 |
> |
return segmentFor(hash).put(key, hash, value, false); |
657 |
|
} |
658 |
|
|
659 |
|
/** |
678 |
|
* <tt>null</tt>. |
679 |
|
* |
680 |
|
**/ |
681 |
< |
public V putIfAbsent(K key, V value) { |
682 |
< |
if (value == null) |
681 |
> |
public V putIfAbsent(K key, V value) { |
682 |
> |
if (value == null) |
683 |
|
throw new NullPointerException(); |
684 |
< |
int hash = hash(key); |
685 |
< |
return segmentFor(hash).put(key, segmentHashFor(hash), value, true); |
684 |
> |
int hash = hash(key); |
685 |
> |
return segmentFor(hash).put(key, hash, value, true); |
686 |
|
} |
687 |
|
|
688 |
|
|
694 |
|
* |
695 |
|
* @param t Mappings to be stored in this map. |
696 |
|
*/ |
697 |
< |
public <K1 extends K, V1 extends V> void putAll(Map<K1,V1> t) { |
698 |
< |
Iterator<Map.Entry<K1,V1>> it = t.entrySet().iterator(); |
697 |
> |
public void putAll(Map<? extends K, ? extends V> t) { |
698 |
> |
Iterator<Map.Entry<? extends K, ? extends V>> it = t.entrySet().iterator(); |
699 |
|
while (it.hasNext()) { |
700 |
< |
Entry<K,V> e = (Entry) it.next(); |
700 |
> |
Entry<? extends K, ? extends V> e = it.next(); |
701 |
|
put(e.getKey(), e.getValue()); |
702 |
|
} |
703 |
|
} |
704 |
|
|
705 |
|
/** |
706 |
< |
* Removes the key (and its corresponding value) from this |
706 |
> |
* Removes the key (and its corresponding value) from this |
707 |
|
* table. This method does nothing if the key is not in the table. |
708 |
|
* |
709 |
|
* @param key the key that needs to be removed. |
714 |
|
*/ |
715 |
|
public V remove(Object key) { |
716 |
|
int hash = hash(key); |
717 |
< |
return segmentFor(hash).remove(key, segmentHashFor(hash), null); |
717 |
> |
return segmentFor(hash).remove(key, hash, null); |
718 |
|
} |
719 |
|
|
720 |
|
/** |
721 |
|
* Removes the (key, value) pair from this |
722 |
|
* table. This method does nothing if the key is not in the table, |
723 |
< |
* or if the key is associated with a different value. |
723 |
> |
* or if the key is associated with a different value. |
724 |
|
* |
725 |
|
* @param key the key that needs to be removed. |
726 |
|
* @param value the associated value. If the value is null, |
730 |
|
* @throws NullPointerException if the key is |
731 |
|
* <code>null</code>. |
732 |
|
*/ |
733 |
< |
public V remove(Object key, Object value) { |
733 |
> |
public boolean remove(Object key, Object value) { |
734 |
|
int hash = hash(key); |
735 |
< |
return segmentFor(hash).remove(key, segmentHashFor(hash), value); |
735 |
> |
return segmentFor(hash).remove(key, hash, value) != null; |
736 |
|
} |
737 |
|
|
738 |
|
/** |
739 |
|
* Removes all mappings from this map. |
740 |
|
*/ |
741 |
|
public void clear() { |
742 |
< |
for (int i = 0; i < segments.length; ++i) |
742 |
> |
for (int i = 0; i < segments.length; ++i) |
743 |
|
segments[i].clear(); |
744 |
|
} |
745 |
|
|
759 |
|
int segs = segments.length; |
760 |
|
int cap = (int)(size() / lf); |
761 |
|
if (cap < segs) cap = segs; |
762 |
< |
ConcurrentHashMap t = new ConcurrentHashMap(cap, lf, segs); |
762 |
> |
ConcurrentHashMap<K,V> t = new ConcurrentHashMap<K,V>(cap, lf, segs); |
763 |
|
t.putAll(this); |
764 |
|
return t; |
765 |
|
} |
772 |
|
* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and |
773 |
|
* <tt>clear</tt> operations. It does not support the <tt>add</tt> or |
774 |
|
* <tt>addAll</tt> operations. |
775 |
+ |
* The returned <tt>iterator</tt> is a "weakly consistent" iterator that |
776 |
+ |
* will never throw {@link java.util.ConcurrentModificationException}, |
777 |
+ |
* and guarantees to traverse elements as they existed upon |
778 |
+ |
* construction of the iterator, and may (but is not guaranteed to) |
779 |
+ |
* reflect any modifications subsequent to construction. |
780 |
|
* |
781 |
|
* @return a set view of the keys contained in this map. |
782 |
|
*/ |
794 |
|
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, |
795 |
|
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations. |
796 |
|
* It does not support the <tt>add</tt> or <tt>addAll</tt> operations. |
797 |
+ |
* The returned <tt>iterator</tt> is a "weakly consistent" iterator that |
798 |
+ |
* will never throw {@link java.util.ConcurrentModificationException}, |
799 |
+ |
* and guarantees to traverse elements as they existed upon |
800 |
+ |
* construction of the iterator, and may (but is not guaranteed to) |
801 |
+ |
* reflect any modifications subsequent to construction. |
802 |
|
* |
803 |
|
* @return a collection view of the values contained in this map. |
804 |
|
*/ |
817 |
|
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, |
818 |
|
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations. |
819 |
|
* It does not support the <tt>add</tt> or <tt>addAll</tt> operations. |
820 |
+ |
* The returned <tt>iterator</tt> is a "weakly consistent" iterator that |
821 |
+ |
* will never throw {@link java.util.ConcurrentModificationException}, |
822 |
+ |
* and guarantees to traverse elements as they existed upon |
823 |
+ |
* construction of the iterator, and may (but is not guaranteed to) |
824 |
+ |
* reflect any modifications subsequent to construction. |
825 |
|
* |
826 |
|
* @return a collection view of the mappings contained in this map. |
827 |
|
*/ |
860 |
|
} |
861 |
|
|
862 |
|
/* ---------------- Iterator Support -------------- */ |
863 |
< |
|
863 |
> |
|
864 |
|
private abstract class HashIterator { |
865 |
|
private int nextSegmentIndex; |
866 |
|
private int nextTableIndex; |
867 |
< |
private HashEntry<K, V>[] currentTable; |
867 |
> |
private HashEntry[] currentTable; |
868 |
|
private HashEntry<K, V> nextEntry; |
869 |
|
private HashEntry<K, V> lastReturned; |
870 |
|
|
879 |
|
private void advance() { |
880 |
|
if (nextEntry != null && (nextEntry = nextEntry.next) != null) |
881 |
|
return; |
882 |
< |
|
882 |
> |
|
883 |
|
while (nextTableIndex >= 0) { |
884 |
< |
if ( (nextEntry = currentTable[nextTableIndex--]) != null) |
884 |
> |
if ( (nextEntry = (HashEntry<K,V>)currentTable[nextTableIndex--]) != null) |
885 |
|
return; |
886 |
|
} |
887 |
< |
|
887 |
> |
|
888 |
|
while (nextSegmentIndex >= 0) { |
889 |
< |
Segment<K,V> seg = segments[nextSegmentIndex--]; |
889 |
> |
Segment<K,V> seg = (Segment<K,V>)segments[nextSegmentIndex--]; |
890 |
|
if (seg.count != 0) { |
891 |
|
currentTable = seg.table; |
892 |
|
for (int j = currentTable.length - 1; j >= 0; --j) { |
893 |
< |
if ( (nextEntry = currentTable[j]) != null) { |
893 |
> |
if ( (nextEntry = (HashEntry<K,V>)currentTable[j]) != null) { |
894 |
|
nextTableIndex = j - 1; |
895 |
|
return; |
896 |
|
} |
964 |
|
} |
965 |
|
} |
966 |
|
|
967 |
< |
private class EntrySet extends AbstractSet { |
967 |
> |
private class EntrySet extends AbstractSet<Map.Entry<K,V>> { |
968 |
|
public Iterator<Map.Entry<K,V>> iterator() { |
969 |
|
return new EntryIterator(); |
970 |
|
} |
979 |
|
if (!(o instanceof Map.Entry)) |
980 |
|
return false; |
981 |
|
Map.Entry<K,V> e = (Map.Entry<K,V>)o; |
982 |
< |
return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()) != null; |
982 |
> |
return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()); |
983 |
|
} |
984 |
|
public int size() { |
985 |
|
return ConcurrentHashMap.this.size(); |
1005 |
|
s.defaultWriteObject(); |
1006 |
|
|
1007 |
|
for (int k = 0; k < segments.length; ++k) { |
1008 |
< |
Segment<K,V> seg = segments[k]; |
1008 |
> |
Segment<K,V> seg = (Segment<K,V>)segments[k]; |
1009 |
|
seg.lock(); |
1010 |
|
try { |
1011 |
< |
HashEntry<K,V>[] tab = seg.table; |
1011 |
> |
HashEntry[] tab = seg.table; |
1012 |
|
for (int i = 0; i < tab.length; ++i) { |
1013 |
< |
for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { |
1013 |
> |
for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i]; e != null; e = e.next) { |
1014 |
|
s.writeObject(e.key); |
1015 |
|
s.writeObject(e.value); |
1016 |
|
} |
1036 |
|
|
1037 |
|
// Initialize each segment to be minimally sized, and let grow. |
1038 |
|
for (int i = 0; i < segments.length; ++i) { |
1039 |
< |
segments[i].setTable(new HashEntry<K,V>[1]); |
1039 |
> |
segments[i].setTable(new HashEntry[1]); |
1040 |
|
} |
1041 |
|
|
1042 |
|
// Read the keys and values, and put the mappings in the table |
1043 |
< |
while (true) { |
1043 |
> |
for (;;) { |
1044 |
|
K key = (K) s.readObject(); |
1045 |
|
V value = (V) s.readObject(); |
1046 |
|
if (key == null) |
1049 |
|
} |
1050 |
|
} |
1051 |
|
} |
1052 |
< |
|
1052 |
> |
|