|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
ConcurrentReaderHashMap.java | 27,8% | 29,7% | 17,5% | 27,6% |
|
1 | /* | |
2 | File: ConcurrentReaderHashMap | |
3 | ||
4 | Written by Doug Lea. Adapted and released, under explicit | |
5 | permission, from JDK1.2 HashMap.java and Hashtable.java which | |
6 | carries the following copyright: | |
7 | ||
8 | * Copyright 1997 by Sun Microsystems, Inc., | |
9 | * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. | |
10 | * All rights reserved. | |
11 | * | |
12 | * This software is the confidential and proprietary information | |
13 | * of Sun Microsystems, Inc. ("Confidential Information"). You | |
14 | * shall not disclose such Confidential Information and shall use | |
15 | * it only in accordance with the terms of the license agreement | |
16 | * you entered into with Sun. | |
17 | ||
18 | History: | |
19 | Date Who What | |
20 | 28oct1999 dl Created | |
21 | 14dec1999 dl jmm snapshot | |
22 | 19apr2000 dl use barrierLock | |
23 | 12jan2001 dl public release | |
24 | 17nov2001 dl Minor tunings | |
25 | 20may2002 dl BarrierLock can now be serialized. | |
26 | 09dec2002 dl Fix interference checks. | |
27 | */ | |
28 | ||
29 | package org.dom4j.tree; | |
30 | ||
31 | import java.io.IOException; | |
32 | import java.io.Serializable; | |
33 | import java.util.AbstractCollection; | |
34 | import java.util.AbstractMap; | |
35 | import java.util.AbstractSet; | |
36 | import java.util.Collection; | |
37 | import java.util.Enumeration; | |
38 | import java.util.Iterator; | |
39 | import java.util.Map; | |
40 | import java.util.NoSuchElementException; | |
41 | import java.util.Set; | |
42 | ||
43 | /** | |
44 | * A version of Hashtable that supports mostly-concurrent reading, but exclusive | |
45 | * writing. Because reads are not limited to periods without writes, a | |
46 | * concurrent reader policy is weaker than a classic reader/writer policy, but | |
47 | * is generally faster and allows more concurrency. This class is a good choice | |
48 | * especially for tables that are mainly created by one thread during the | |
49 | * start-up phase of a program, and from then on, are mainly read (with perhaps | |
50 | * occasional additions or removals) in many threads. If you also need | |
51 | * concurrency among writes, consider instead using ConcurrentHashMap. | |
52 | * <p> | |
53 | * | |
54 | * Successful retrievals using get(key) and containsKey(key) usually run without | |
55 | * locking. Unsuccessful ones (i.e., when the key is not present) do involve | |
56 | * brief synchronization (locking). Also, the size and isEmpty methods are | |
57 | * always synchronized. | |
58 | * | |
59 | * <p> | |
60 | * Because retrieval operations can ordinarily overlap with writing operations | |
61 | * (i.e., put, remove, and their derivatives), retrievals can only be guaranteed | |
62 | * to return the results of the most recently <em>completed</em> operations | |
63 | * holding upon their onset. Retrieval operations may or may not return results | |
64 | * reflecting in-progress writing operations. However, the retrieval operations | |
65 | * do always return consistent results -- either those holding before any single | |
66 | * modification or after it, but never a nonsense result. For aggregate | |
67 | * operations such as putAll and clear, concurrent reads may reflect insertion | |
68 | * or removal of only some entries. In those rare contexts in which you use a | |
69 | * hash table to synchronize operations across threads (for example, to prevent | |
70 | * reads until after clears), you should either encase operations in | |
71 | * synchronized blocks, or instead use java.util.Hashtable. | |
72 | * | |
73 | * <p> | |
74 | * | |
75 | * This class also supports optional guaranteed exclusive reads, simply by | |
76 | * surrounding a call within a synchronized block, as in <br> | |
77 | * <code>ConcurrentReaderHashMap t; ... Object v; <br> | |
78 | * synchronized(t) { v = t.get(k); } </code><br> | |
79 | * | |
80 | * But this is not usually necessary in practice. For example, it is generally | |
81 | * inefficient to write: | |
82 | * | |
83 | * <pre> | |
84 | * | |
85 | * | |
86 | * ConcurrentReaderHashMap t; ... // Inefficient version | |
87 | * Object key; ... | |
88 | * Object value; ... | |
89 | * synchronized(t) { | |
90 | * if (!t.containsKey(key)) | |
91 | * t.put(key, value); | |
92 | * // other code if not previously present | |
93 | * } | |
94 | * else { | |
95 | * // other code if it was previously present | |
96 | * } | |
97 | * } | |
98 | * | |
99 | * | |
100 | * </pre> | |
101 | * | |
102 | * Instead, if the values are intended to be the same in each case, just take | |
103 | * advantage of the fact that put returns null if the key was not previously | |
104 | * present: | |
105 | * | |
106 | * <pre> | |
107 | * | |
108 | * | |
109 | * ConcurrentReaderHashMap t; ... // Use this instead | |
110 | * Object key; ... | |
111 | * Object value; ... | |
112 | * Object oldValue = t.put(key, value); | |
113 | * if (oldValue == null) { | |
114 | * // other code if not previously present | |
115 | * } | |
116 | * else { | |
117 | * // other code if it was previously present | |
118 | * } | |
119 | * | |
120 | * | |
121 | * </pre> | |
122 | * | |
123 | * <p> | |
124 | * | |
125 | * Iterators and Enumerations (i.e., those returned by keySet().iterator(), | |
126 | * entrySet().iterator(), values().iterator(), keys(), and elements()) return | |
127 | * elements reflecting the state of the hash table at some point at or since the | |
128 | * creation of the iterator/enumeration. They will return at most one instance | |
129 | * of each element (via next()/nextElement()), but might or might not reflect | |
130 | * puts and removes that have been processed since they were created. They do | |
131 | * <em>not</em> throw ConcurrentModificationException. However, these | |
132 | * iterators are designed to be used by only one thread at a time. Sharing an | |
133 | * iterator across multiple threads may lead to unpredictable results if the | |
134 | * table is being concurrently modified. Again, you can ensure interference-free | |
135 | * iteration by enclosing the iteration in a synchronized block. | |
136 | * <p> | |
137 | * | |
138 | * This class may be used as a direct replacement for any use of | |
139 | * java.util.Hashtable that does not depend on readers being blocked during | |
140 | * updates. Like Hashtable but unlike java.util.HashMap, this class does NOT | |
141 | * allow <tt>null</tt> to be used as a key or value. This class is also | |
142 | * typically faster than ConcurrentHashMap when there is usually only one thread | |
143 | * updating the table, but possibly many retrieving values from it. | |
144 | * <p> | |
145 | * | |
146 | * Implementation note: A slightly faster implementation of this class will be | |
147 | * possible once planned Java Memory Model revisions are in place. | |
148 | * | |
149 | * <p>[ <a | |
150 | * href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> | |
151 | * Introduction to this package. </a>] | |
152 | * | |
153 | */ | |
154 | ||
155 | class ConcurrentReaderHashMap extends AbstractMap implements Map, Cloneable, | |
156 | Serializable { | |
157 | ||
158 | /* | |
159 | * The basic strategy is an optimistic-style scheme based on the guarantee | |
160 | * that the hash table and its lists are always kept in a consistent enough | |
161 | * state to be read without locking: | |
162 | * | |
163 | * Read operations first proceed without locking, by traversing the | |
164 | * apparently correct list of the apparently correct bin. If an entry is | |
165 | * found, but not invalidated (value field null), it is returned. If not | |
166 | * found, operations must recheck (after a memory barrier) to make sure they | |
167 | * are using both the right list and the right table (which can change under | |
168 | * resizes). If invalidated, reads must acquire main update lock to wait out | |
169 | * the update, and then re-traverse. | |
170 | * | |
171 | * All list additions are at the front of each bin, making it easy to check | |
172 | * changes, and also fast to traverse. Entry next pointers are never | |
173 | * assigned. Remove() builds new nodes when necessary to preserve this. | |
174 | * | |
175 | * Remove() (also clear()) invalidates removed nodes to alert read | |
176 | * operations that they must wait out the full modifications. | |
177 | * | |
178 | */ | |
179 | ||
180 | /** A Serializable class for barrier lock * */ | |
181 | protected static class BarrierLock implements java.io.Serializable { | |
182 | } | |
183 | ||
184 | /** | |
185 | * Lock used only for its memory effects. | |
186 | */ | |
187 | protected final BarrierLock barrierLock = new BarrierLock(); | |
188 | ||
189 | /** | |
190 | * field written to only to guarantee lock ordering. | |
191 | */ | |
192 | ||
193 | protected transient Object lastWrite; | |
194 | ||
195 | /** | |
196 | * Force a memory synchronization that will cause all readers to see table. | |
197 | * Call only when already holding main synch lock. | |
198 | */ | |
199 | 20624 | protected final void recordModification(Object x) { |
200 | 20624 | synchronized (barrierLock) { |
201 | 20624 | lastWrite = x; |
202 | } | |
203 | } | |
204 | ||
205 | /** | |
206 | * Get ref to table; the reference and the cells it accesses will be at | |
207 | * least as fresh as from last use of barrierLock | |
208 | */ | |
209 | 41248 | protected final Entry[] getTableForReading() { |
210 | 41248 | synchronized (barrierLock) { |
211 | 41248 | return table; |
212 | } | |
213 | } | |
214 | ||
215 | /** | |
216 | * The default initial number of table slots for this table (32). Used when | |
217 | * not otherwise specified in constructor. | |
218 | */ | |
219 | public static int DEFAULT_INITIAL_CAPACITY = 32; | |
220 | ||
221 | /** | |
222 | * The minimum capacity, used if a lower value is implicitly specified by | |
223 | * either of the constructors with arguments. MUST be a power of two. | |
224 | */ | |
225 | private static final int MINIMUM_CAPACITY = 4; | |
226 | ||
227 | /** | |
228 | * The maximum capacity, used if a higher value is implicitly specified by | |
229 | * either of the constructors with arguments. MUST be a power of two <= 1 < | |
230 | * <30. | |
231 | */ | |
232 | private static final int MAXIMUM_CAPACITY = 1 << 30; | |
233 | ||
234 | /** | |
235 | * The default load factor for this table (1.0). Used when not otherwise | |
236 | * specified in constructor. | |
237 | */ | |
238 | ||
239 | public static final float DEFAULT_LOAD_FACTOR = 0.75f; | |
240 | ||
241 | /** | |
242 | * The hash table data. | |
243 | */ | |
244 | protected transient Entry[] table; | |
245 | ||
246 | /** | |
247 | * The total number of mappings in the hash table. | |
248 | */ | |
249 | protected transient int count; | |
250 | ||
251 | /** | |
252 | * The table is rehashed when its size exceeds this threshold. (The value of | |
253 | * this field is always (int)(capacity * loadFactor).) | |
254 | * | |
255 | * @serial | |
256 | */ | |
257 | protected int threshold; | |
258 | ||
259 | /** | |
260 | * The load factor for the hash table. | |
261 | * | |
262 | * @serial | |
263 | */ | |
264 | protected float loadFactor; | |
265 | ||
266 | /** | |
267 | * Returns the appropriate capacity (power of two) for the specified initial | |
268 | * capacity argument. | |
269 | */ | |
270 | 10476 | private int p2capacity(int initialCapacity) { |
271 | 10476 | int cap = initialCapacity; |
272 | ||
273 | // Compute the appropriate capacity | |
274 | 10476 | int result; |
275 | 10476 | if (cap > MAXIMUM_CAPACITY || cap < 0) { |
276 | 0 | result = MAXIMUM_CAPACITY; |
277 | } else { | |
278 | 10476 | result = MINIMUM_CAPACITY; |
279 | 10476 | while (result < cap) |
280 | 31428 | result <<= 1; |
281 | } | |
282 | 10476 | return result; |
283 | } | |
284 | ||
285 | /** | |
286 | * Return hash code for Object x. Since we are using power-of-two tables, it | |
287 | * is worth the effort to improve hashcode via the same multiplicative | |
288 | * scheme as used in IdentityHashMap. | |
289 | */ | |
290 | 2283990 | private static int hash(Object x) { |
291 | 2283990 | int h = x.hashCode(); |
292 | // Multiply by 127 (quickly, via shifts), and mix in some high | |
293 | // bits to help guard against bunching of codes that are | |
294 | // consecutive or equally spaced. | |
295 | 2283990 | return ((h << 7) - h + (h >>> 9) + (h >>> 17)); |
296 | } | |
297 | ||
298 | /** | |
299 | * Check for equality of non-null references x and y. | |
300 | */ | |
301 | 2222118 | protected boolean eq(Object x, Object y) { |
302 | 2222118 | return x == y || x.equals(y); |
303 | } | |
304 | ||
305 | /** | |
306 | * Constructs a new, empty map with the specified initial capacity and the | |
307 | * specified load factor. | |
308 | * | |
309 | * @param initialCapacity | |
310 | * the initial capacity The actual initial capacity is rounded to | |
311 | * the nearest power of two. | |
312 | * @param loadFactor | |
313 | * the load factor of the ConcurrentReaderHashMap | |
314 | * @throws IllegalArgumentException | |
315 | * if the initial maximum number of elements is less than zero, | |
316 | * or if the load factor is nonpositive. | |
317 | */ | |
318 | ||
319 | 10476 | public ConcurrentReaderHashMap(int initialCapacity, float loadFactor) { |
320 | 10476 | if (loadFactor <= 0) |
321 | 0 | throw new IllegalArgumentException("Illegal Load factor: " |
322 | + loadFactor); | |
323 | 10476 | this.loadFactor = loadFactor; |
324 | ||
325 | 10476 | int cap = p2capacity(initialCapacity); |
326 | ||
327 | 10476 | table = new Entry[cap]; |
328 | 10476 | threshold = (int) (cap * loadFactor); |
329 | } | |
330 | ||
331 | /** | |
332 | * Constructs a new, empty map with the specified initial capacity and | |
333 | * default load factor. | |
334 | * | |
335 | * @param initialCapacity | |
336 | * the initial capacity of the ConcurrentReaderHashMap. | |
337 | * @throws IllegalArgumentException | |
338 | * if the initial maximum number of elements is less than zero. | |
339 | */ | |
340 | ||
341 | 0 | public ConcurrentReaderHashMap(int initialCapacity) { |
342 | 0 | this(initialCapacity, DEFAULT_LOAD_FACTOR); |
343 | } | |
344 | ||
345 | /** | |
346 | * Constructs a new, empty map with a default initial capacity and load | |
347 | * factor. | |
348 | */ | |
349 | ||
350 | 10476 | public ConcurrentReaderHashMap() { |
351 | 10476 | this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); |
352 | } | |
353 | ||
354 | /** | |
355 | * Constructs a new map with the same mappings as the given map. The map is | |
356 | * created with a capacity of twice the number of mappings in the given map | |
357 | * or 16 (whichever is greater), and a default load factor. | |
358 | */ | |
359 | ||
360 | 0 | public ConcurrentReaderHashMap(Map t) { |
361 | 0 | this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, 16), |
362 | DEFAULT_LOAD_FACTOR); | |
363 | 0 | putAll(t); |
364 | } | |
365 | ||
366 | /** | |
367 | * Returns the number of key-value mappings in this map. | |
368 | * | |
369 | * @return the number of key-value mappings in this map. | |
370 | */ | |
371 | ||
372 | 0 | public synchronized int size() { |
373 | 0 | return count; |
374 | } | |
375 | ||
376 | /** | |
377 | * Returns <tt>true</tt> if this map contains no key-value mappings. | |
378 | * | |
379 | * @return <tt>true</tt> if this map contains no key-value mappings. | |
380 | */ | |
381 | ||
382 | 0 | public synchronized boolean isEmpty() { |
383 | 0 | return count == 0; |
384 | } | |
385 | ||
386 | /** | |
387 | * Returns the value to which the specified key is mapped in this table. | |
388 | * | |
389 | * @param key | |
390 | * a key in the table. | |
391 | * @return the value to which the key is mapped in this table; | |
392 | * <code>null</code> if the key is not mapped to any value in this | |
393 | * table. | |
394 | * @exception NullPointerException | |
395 | * if the key is <code>null</code>. | |
396 | * @see #put(Object, Object) | |
397 | */ | |
398 | ||
399 | 2263360 | public Object get(Object key) { |
400 | ||
401 | // throw null pointer exception if key null | |
402 | 2263360 | int hash = hash(key); |
403 | ||
404 | /* | |
405 | * Start off at the apparently correct bin. If entry is found, we need | |
406 | * to check after a barrier anyway. If not found, we need a barrier to | |
407 | * check if we are actually in right bin. So either way, we encounter | |
408 | * only one barrier unless we need to retry. And we only need to fully | |
409 | * synchronize if there have been concurrent modifications. | |
410 | */ | |
411 | ||
412 | 2263360 | Entry[] tab = table; |
413 | 2263360 | int index = hash & (tab.length - 1); |
414 | 2263360 | Entry first = tab[index]; |
415 | 2263360 | Entry e = first; |
416 | ||
417 | 2263360 | for (;;) { |
418 | 2272846 | if (e == null) { |
419 | ||
420 | // If key apparently not there, check to | |
421 | // make sure this was a valid read | |
422 | ||
423 | 41248 | Entry[] reread = getTableForReading(); |
424 | 41248 | if (tab == reread && first == tab[index]) |
425 | 41248 | return null; |
426 | else { | |
427 | // Wrong list -- must restart traversal at new first | |
428 | 0 | tab = reread; |
429 | 0 | e = first = tab[index = hash & (tab.length - 1)]; |
430 | } | |
431 | ||
432 | } | |
433 | ||
434 | 2231598 | else if (e.hash == hash && eq(key, e.key)) { |
435 | 2222112 | Object value = e.value; |
436 | 2222112 | if (value != null) |
437 | 2222112 | return value; |
438 | ||
439 | // Entry was invalidated during deletion. But it could | |
440 | // have been re-inserted, so we must retraverse. | |
441 | // To avoid useless contention, get lock to wait out | |
442 | // modifications | |
443 | // before retraversing. | |
444 | ||
445 | 0 | synchronized (this) { |
446 | 0 | tab = table; |
447 | } | |
448 | 0 | e = first = tab[index = hash & (tab.length - 1)]; |
449 | ||
450 | } else | |
451 | 9486 | e = e.next; |
452 | } | |
453 | } | |
454 | ||
455 | /** | |
456 | * Tests if the specified object is a key in this table. | |
457 | * | |
458 | * @param key | |
459 | * possible key. | |
460 | * @return <code>true</code> if and only if the specified object is a key | |
461 | * in this table, as determined by the <tt>equals</tt> method; | |
462 | * <code>false</code> otherwise. | |
463 | * @exception NullPointerException | |
464 | * if the key is <code>null</code>. | |
465 | * @see #contains(Object) | |
466 | */ | |
467 | ||
468 | 0 | public boolean containsKey(Object key) { |
469 | 0 | return get(key) != null; |
470 | } | |
471 | ||
472 | /** | |
473 | * Maps the specified <code>key</code> to the specified <code>value</code> | |
474 | * in this table. Neither the key nor the value can be <code>null</code>. | |
475 | * <p> | |
476 | * | |
477 | * The value can be retrieved by calling the <code>get</code> method with | |
478 | * a key that is equal to the original key. | |
479 | * | |
480 | * @param key | |
481 | * the table key. | |
482 | * @param value | |
483 | * the value. | |
484 | * @return the previous value of the specified key in this table, or | |
485 | * <code>null</code> if it did not have one. | |
486 | * @exception NullPointerException | |
487 | * if the key or value is <code>null</code>. | |
488 | * @see Object#equals(Object) | |
489 | * @see #get(Object) | |
490 | */ | |
491 | ||
492 | 20630 | public Object put(Object key, Object value) { |
493 | 20630 | if (value == null) |
494 | 0 | throw new NullPointerException(); |
495 | ||
496 | 20630 | int hash = hash(key); |
497 | 20630 | Entry[] tab = table; |
498 | 20630 | int index = hash & (tab.length - 1); |
499 | 20630 | Entry first = tab[index]; |
500 | 20630 | Entry e; |
501 | ||
502 | 20630 | for (e = first; e != null; e = e.next) |
503 | 4289 | if (e.hash == hash && eq(key, e.key)) |
504 | 6 | break; |
505 | ||
506 | 20630 | synchronized (this) { |
507 | 20630 | if (tab == table) { |
508 | 20630 | if (e == null) { |
509 | // make sure we are adding to correct list | |
510 | 20624 | if (first == tab[index]) { |
511 | // Add to front of list | |
512 | 20624 | Entry newEntry = new Entry(hash, key, value, first); |
513 | 20624 | tab[index] = newEntry; |
514 | 20624 | if (++count >= threshold) |
515 | 9 | rehash(); |
516 | else | |
517 | 20615 | recordModification(newEntry); |
518 | 20624 | return null; |
519 | } | |
520 | } else { | |
521 | 6 | Object oldValue = e.value; |
522 | 6 | if (first == tab[index] && oldValue != null) { |
523 | 6 | e.value = value; |
524 | 6 | return oldValue; |
525 | } | |
526 | } | |
527 | } | |
528 | ||
529 | // retry if wrong list or lost race against concurrent remove | |
530 | 0 | return sput(key, value, hash); |
531 | } | |
532 | } | |
533 | ||
534 | /** | |
535 | * Continuation of put(), called only when synch lock is held and | |
536 | * interference has been detected. | |
537 | */ | |
538 | 0 | protected Object sput(Object key, Object value, int hash) { |
539 | ||
540 | 0 | Entry[] tab = table; |
541 | 0 | int index = hash & (tab.length - 1); |
542 | 0 | Entry first = tab[index]; |
543 | 0 | Entry e = first; |
544 | ||
545 | 0 | for (;;) { |
546 | 0 | if (e == null) { |
547 | 0 | Entry newEntry = new Entry(hash, key, value, first); |
548 | 0 | tab[index] = newEntry; |
549 | 0 | if (++count >= threshold) |
550 | 0 | rehash(); |
551 | else | |
552 | 0 | recordModification(newEntry); |
553 | 0 | return null; |
554 | 0 | } else if (e.hash == hash && eq(key, e.key)) { |
555 | 0 | Object oldValue = e.value; |
556 | 0 | e.value = value; |
557 | 0 | return oldValue; |
558 | } else | |
559 | 0 | e = e.next; |
560 | } | |
561 | } | |
562 | ||
563 | /** | |
564 | * Rehashes the contents of this map into a new table with a larger | |
565 | * capacity. This method is called automatically when the number of keys in | |
566 | * this map exceeds its capacity and load factor. | |
567 | */ | |
568 | 9 | protected void rehash() { |
569 | 9 | Entry[] oldTable = table; |
570 | 9 | int oldCapacity = oldTable.length; |
571 | 9 | if (oldCapacity >= MAXIMUM_CAPACITY) { |
572 | 0 | threshold = Integer.MAX_VALUE; // avoid retriggering |
573 | 0 | return; |
574 | } | |
575 | ||
576 | 9 | int newCapacity = oldCapacity << 1; |
577 | 9 | int mask = newCapacity - 1; |
578 | 9 | threshold = (int) (newCapacity * loadFactor); |
579 | ||
580 | 9 | Entry[] newTable = new Entry[newCapacity]; |
581 | /* | |
582 | * Reclassify nodes in each list to new Map. Because we are using | |
583 | * power-of-two expansion, the elements from each bin must either stay | |
584 | * at same index, or move to oldCapacity+index. We also eliminate | |
585 | * unnecessary node creation by catching cases where old nodes can be | |
586 | * reused because their next fields won't change. Statistically, at the | |
587 | * default threshhold, only about one-sixth of them need cloning. (The | |
588 | * nodes they replace will be garbage collectable as soon as they are no | |
589 | * longer referenced by any reader thread that may be in the midst of | |
590 | * traversing table right now.) | |
591 | */ | |
592 | ||
593 | 9 | for (int i = 0; i < oldCapacity; i++) { |
594 | // We need to guarantee that any existing reads of old Map can | |
595 | // proceed. So we cannot yet null out each bin. | |
596 | 16352 | Entry e = oldTable[i]; |
597 | ||
598 | 16352 | if (e != null) { |
599 | 8821 | int idx = e.hash & mask; |
600 | 8821 | Entry next = e.next; |
601 | ||
602 | // Single node on list | |
603 | 8821 | if (next == null) |
604 | 5980 | newTable[idx] = e; |
605 | ||
606 | else { | |
607 | // Reuse trailing consecutive sequence of all same bit | |
608 | 2841 | Entry lastRun = e; |
609 | 2841 | int lastIdx = idx; |
610 | 2841 | for (Entry last = next; last != null; last = last.next) { |
611 | 3443 | int k = last.hash & mask; |
612 | 3443 | if (k != lastIdx) { |
613 | 1921 | lastIdx = k; |
614 | 1921 | lastRun = last; |
615 | } | |
616 | } | |
617 | 2841 | newTable[lastIdx] = lastRun; |
618 | ||
619 | // Clone all remaining nodes | |
620 | 2841 | for (Entry p = e; p != lastRun; p = p.next) { |
621 | 2116 | int k = p.hash & mask; |
622 | 2116 | newTable[k] = new Entry(p.hash, p.key, p.value, |
623 | newTable[k]); | |
624 | } | |
625 | } | |
626 | } | |
627 | } | |
628 | ||
629 | 9 | table = newTable; |
630 | 9 | recordModification(newTable); |
631 | } | |
632 | ||
633 | /** | |
634 | * Removes the key (and its corresponding value) from this table. This | |
635 | * method does nothing if the key is not in the table. | |
636 | * | |
637 | * @param key | |
638 | * the key that needs to be removed. | |
639 | * @return the value to which the key had been mapped in this table, or | |
640 | * <code>null</code> if the key did not have a mapping. | |
641 | * @exception NullPointerException | |
642 | * if the key is <code>null</code>. | |
643 | */ | |
644 | ||
645 | 0 | public Object remove(Object key) { |
646 | /* | |
647 | * Find the entry, then 1. Set value field to null, to force get() to | |
648 | * retry 2. Rebuild the list without this entry. All entries following | |
649 | * removed node can stay in list, but all preceeding ones need to be | |
650 | * cloned. Traversals rely on this strategy to ensure that elements will | |
651 | * not be repeated during iteration. | |
652 | */ | |
653 | ||
654 | 0 | int hash = hash(key); |
655 | 0 | Entry[] tab = table; |
656 | 0 | int index = hash & (tab.length - 1); |
657 | 0 | Entry first = tab[index]; |
658 | 0 | Entry e = first; |
659 | ||
660 | 0 | for (e = first; e != null; e = e.next) |
661 | 0 | if (e.hash == hash && eq(key, e.key)) |
662 | 0 | break; |
663 | ||
664 | 0 | synchronized (this) { |
665 | 0 | if (tab == table) { |
666 | 0 | if (e == null) { |
667 | 0 | if (first == tab[index]) |
668 | 0 | return null; |
669 | } else { | |
670 | 0 | Object oldValue = e.value; |
671 | 0 | if (first == tab[index] && oldValue != null) { |
672 | 0 | e.value = null; |
673 | 0 | count--; |
674 | ||
675 | 0 | Entry head = e.next; |
676 | 0 | for (Entry p = first; p != e; p = p.next) |
677 | 0 | head = new Entry(p.hash, p.key, p.value, head); |
678 | ||
679 | 0 | tab[index] = head; |
680 | 0 | recordModification(head); |
681 | 0 | return oldValue; |
682 | } | |
683 | } | |
684 | } | |
685 | ||
686 | // Wrong list or interference | |
687 | 0 | return sremove(key, hash); |
688 | } | |
689 | } | |
690 | ||
691 | /** | |
692 | * Continuation of remove(), called only when synch lock is held and | |
693 | * interference has been detected. | |
694 | */ | |
695 | ||
696 | 0 | protected Object sremove(Object key, int hash) { |
697 | 0 | Entry[] tab = table; |
698 | 0 | int index = hash & (tab.length - 1); |
699 | 0 | Entry first = tab[index]; |
700 | ||
701 | 0 | for (Entry e = first; e != null; e = e.next) { |
702 | 0 | if (e.hash == hash && eq(key, e.key)) { |
703 | 0 | Object oldValue = e.value; |
704 | 0 | e.value = null; |
705 | 0 | count--; |
706 | 0 | Entry head = e.next; |
707 | 0 | for (Entry p = first; p != e; p = p.next) |
708 | 0 | head = new Entry(p.hash, p.key, p.value, head); |
709 | ||
710 | 0 | tab[index] = head; |
711 | 0 | recordModification(head); |
712 | 0 | return oldValue; |
713 | } | |
714 | } | |
715 | 0 | return null; |
716 | } | |
717 | ||
718 | /** | |
719 | * Returns <tt>true</tt> if this map maps one or more keys to the | |
720 | * specified value. Note: This method requires a full internal traversal of | |
721 | * the hash table, and so is much slower than method <tt>containsKey</tt>. | |
722 | * | |
723 | * @param value | |
724 | * value whose presence in this map is to be tested. | |
725 | * @return <tt>true</tt> if this map maps one or more keys to the | |
726 | * specified value. | |
727 | * @exception NullPointerException | |
728 | * if the value is <code>null</code>. | |
729 | */ | |
730 | ||
731 | 0 | public boolean containsValue(Object value) { |
732 | 0 | if (value == null) |
733 | 0 | throw new NullPointerException(); |
734 | ||
735 | 0 | Entry tab[] = getTableForReading(); |
736 | ||
737 | 0 | for (int i = 0; i < tab.length; ++i) { |
738 | 0 | for (Entry e = tab[i]; e != null; e = e.next) |
739 | 0 | if (value.equals(e.value)) |
740 | 0 | return true; |
741 | } | |
742 | ||
743 | 0 | return false; |
744 | } | |
745 | ||
746 | /** | |
747 | * Tests if some key maps into the specified value in this table. This | |
748 | * operation is more expensive than the <code>containsKey</code> method. | |
749 | * <p> | |
750 | * | |
751 | * Note that this method is identical in functionality to containsValue, | |
752 | * (which is part of the Map interface in the collections framework). | |
753 | * | |
754 | * @param value | |
755 | * a value to search for. | |
756 | * @return <code>true</code> if and only if some key maps to the | |
757 | * <code>value</code> argument in this table as determined by the | |
758 | * <tt>equals</tt> method; <code>false</code> otherwise. | |
759 | * @exception NullPointerException | |
760 | * if the value is <code>null</code>. | |
761 | * @see #containsKey(Object) | |
762 | * @see #containsValue(Object) | |
763 | * @see Map | |
764 | */ | |
765 | ||
766 | 0 | public boolean contains(Object value) { |
767 | 0 | return containsValue(value); |
768 | } | |
769 | ||
770 | /** | |
771 | * Copies all of the mappings from the specified map to this one. | |
772 | * | |
773 | * These mappings replace any mappings that this map had for any of the keys | |
774 | * currently in the specified Map. | |
775 | * | |
776 | * @param t | |
777 | * Mappings to be stored in this map. | |
778 | */ | |
779 | ||
780 | 0 | public synchronized void putAll(Map t) { |
781 | 0 | int n = t.size(); |
782 | 0 | if (n == 0) |
783 | 0 | return; |
784 | ||
785 | // Expand enough to hold at least n elements without resizing. | |
786 | // We can only resize table by factor of two at a time. | |
787 | // It is faster to rehash with fewer elements, so do it now. | |
788 | 0 | while (n >= threshold) |
789 | 0 | rehash(); |
790 | ||
791 | 0 | for (Iterator it = t.entrySet().iterator(); it.hasNext();) { |
792 | 0 | Map.Entry entry = (Map.Entry) it.next(); |
793 | 0 | Object key = entry.getKey(); |
794 | 0 | Object value = entry.getValue(); |
795 | 0 | put(key, value); |
796 | } | |
797 | } | |
798 | ||
799 | /** | |
800 | * Removes all mappings from this map. | |
801 | */ | |
802 | 0 | public synchronized void clear() { |
803 | 0 | Entry tab[] = table; |
804 | 0 | for (int i = 0; i < tab.length; ++i) { |
805 | ||
806 | // must invalidate all to force concurrent get's to wait and then | |
807 | // retry | |
808 | 0 | for (Entry e = tab[i]; e != null; e = e.next) |
809 | 0 | e.value = null; |
810 | ||
811 | 0 | tab[i] = null; |
812 | } | |
813 | 0 | count = 0; |
814 | 0 | recordModification(tab); |
815 | } | |
816 | ||
817 | /** | |
818 | * Returns a shallow copy of this <tt>ConcurrentReaderHashMap</tt> | |
819 | * instance: the keys and values themselves are not cloned. | |
820 | * | |
821 | * @return a shallow copy of this map. | |
822 | */ | |
823 | ||
824 | 0 | public synchronized Object clone() { |
825 | 0 | try { |
826 | 0 | ConcurrentReaderHashMap t = (ConcurrentReaderHashMap) super.clone(); |
827 | ||
828 | 0 | t.keySet = null; |
829 | 0 | t.entrySet = null; |
830 | 0 | t.values = null; |
831 | ||
832 | 0 | Entry[] tab = table; |
833 | 0 | t.table = new Entry[tab.length]; |
834 | 0 | Entry[] ttab = t.table; |
835 | ||
836 | 0 | for (int i = 0; i < tab.length; ++i) { |
837 | 0 | Entry first = null; |
838 | 0 | for (Entry e = tab[i]; e != null; e = e.next) |
839 | 0 | first = new Entry(e.hash, e.key, e.value, first); |
840 | 0 | ttab[i] = first; |
841 | } | |
842 | ||
843 | 0 | return t; |
844 | } catch (CloneNotSupportedException e) { | |
845 | // this shouldn't happen, since we are Cloneable | |
846 | 0 | throw new InternalError(); |
847 | } | |
848 | } | |
849 | ||
850 | // Views | |
851 | ||
852 | protected transient Set keySet = null; | |
853 | ||
854 | protected transient Set entrySet = null; | |
855 | ||
856 | protected transient Collection values = null; | |
857 | ||
858 | /** | |
859 | * Returns a set view of the keys contained in this map. The set is backed | |
860 | * by the map, so changes to the map are reflected in the set, and | |
861 | * vice-versa. The set supports element removal, which removes the | |
862 | * corresponding mapping from this map, via the <tt>Iterator.remove</tt>, | |
863 | * <tt>Set.remove</tt>,<tt>removeAll</tt>,<tt>retainAll</tt>, and | |
864 | * <tt>clear</tt> operations. It does not support the <tt>add</tt> or | |
865 | * <tt>addAll</tt> operations. | |
866 | * | |
867 | * @return a set view of the keys contained in this map. | |
868 | */ | |
869 | ||
870 | 0 | public Set keySet() { |
871 | 0 | Set ks = keySet; |
872 | 0 | return (ks != null) ? ks : (keySet = new KeySet()); |
873 | } | |
874 | ||
875 | private class KeySet extends AbstractSet { | |
876 | 0 | public Iterator iterator() { |
877 | 0 | return new KeyIterator(); |
878 | } | |
879 | ||
880 | 0 | public int size() { |
881 | 0 | return ConcurrentReaderHashMap.this.size(); |
882 | } | |
883 | ||
884 | 0 | public boolean contains(Object o) { |
885 | 0 | return ConcurrentReaderHashMap.this.containsKey(o); |
886 | } | |
887 | ||
888 | 0 | public boolean remove(Object o) { |
889 | 0 | return ConcurrentReaderHashMap.this.remove(o) != null; |
890 | } | |
891 | ||
892 | 0 | public void clear() { |
893 | 0 | ConcurrentReaderHashMap.this.clear(); |
894 | } | |
895 | } | |
896 | ||
897 | /** | |
898 | * Returns a collection view of the values contained in this map. The | |
899 | * collection is backed by the map, so changes to the map are reflected in | |
900 | * the collection, and vice-versa. The collection supports element removal, | |
901 | * which removes the corresponding mapping from this map, via the | |
902 | * <tt>Iterator.remove</tt>,<tt>Collection.remove</tt>, | |
903 | * <tt>removeAll</tt>,<tt>retainAll</tt>, and <tt>clear</tt> | |
904 | * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> | |
905 | * operations. | |
906 | * | |
907 | * @return a collection view of the values contained in this map. | |
908 | */ | |
909 | ||
910 | 0 | public Collection values() { |
911 | 0 | Collection vs = values; |
912 | 0 | return (vs != null) ? vs : (values = new Values()); |
913 | } | |
914 | ||
915 | private class Values extends AbstractCollection { | |
916 | 0 | public Iterator iterator() { |
917 | 0 | return new ValueIterator(); |
918 | } | |
919 | ||
920 | 0 | public int size() { |
921 | 0 | return ConcurrentReaderHashMap.this.size(); |
922 | } | |
923 | ||
924 | 0 | public boolean contains(Object o) { |
925 | 0 | return ConcurrentReaderHashMap.this.containsValue(o); |
926 | } | |
927 | ||
928 | 0 | public void clear() { |
929 | 0 | ConcurrentReaderHashMap.this.clear(); |
930 | } | |
931 | } | |
932 | ||
933 | /** | |
934 | * Returns a collection view of the mappings contained in this map. Each | |
935 | * element in the returned collection is a <tt>Map.Entry</tt>. The | |
936 | * collection is backed by the map, so changes to the map are reflected in | |
937 | * the collection, and vice-versa. The collection supports element removal, | |
938 | * which removes the corresponding mapping from the map, via the | |
939 | * <tt>Iterator.remove</tt>,<tt>Collection.remove</tt>, | |
940 | * <tt>removeAll</tt>,<tt>retainAll</tt>, and <tt>clear</tt> | |
941 | * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> | |
942 | * operations. | |
943 | * | |
944 | * @return a collection view of the mappings contained in this map. | |
945 | */ | |
946 | ||
947 | 0 | public Set entrySet() { |
948 | 0 | Set es = entrySet; |
949 | 0 | return (es != null) ? es : (entrySet = new EntrySet()); |
950 | } | |
951 | ||
952 | private class EntrySet extends AbstractSet { | |
953 | 0 | public Iterator iterator() { |
954 | 0 | return new HashIterator(); |
955 | } | |
956 | ||
957 | 0 | public boolean contains(Object o) { |
958 | 0 | if (!(o instanceof Map.Entry)) |
959 | 0 | return false; |
960 | 0 | Map.Entry entry = (Map.Entry) o; |
961 | 0 | Object v = ConcurrentReaderHashMap.this.get(entry.getKey()); |
962 | 0 | return v != null && v.equals(entry.getValue()); |
963 | } | |
964 | ||
965 | 0 | public boolean remove(Object o) { |
966 | 0 | if (!(o instanceof Map.Entry)) |
967 | 0 | return false; |
968 | 0 | return ConcurrentReaderHashMap.this |
969 | .findAndRemoveEntry((Map.Entry) o); | |
970 | } | |
971 | ||
972 | 0 | public int size() { |
973 | 0 | return ConcurrentReaderHashMap.this.size(); |
974 | } | |
975 | ||
976 | 0 | public void clear() { |
977 | 0 | ConcurrentReaderHashMap.this.clear(); |
978 | } | |
979 | } | |
980 | ||
981 | /** | |
982 | * Helper method for entrySet.remove | |
983 | */ | |
984 | 0 | protected synchronized boolean findAndRemoveEntry(Map.Entry entry) { |
985 | 0 | Object key = entry.getKey(); |
986 | 0 | Object v = get(key); |
987 | 0 | if (v != null && v.equals(entry.getValue())) { |
988 | 0 | remove(key); |
989 | 0 | return true; |
990 | } else | |
991 | 0 | return false; |
992 | } | |
993 | ||
994 | /** | |
995 | * Returns an enumeration of the keys in this table. | |
996 | * | |
997 | * @return an enumeration of the keys in this table. | |
998 | * @see Enumeration | |
999 | * @see #elements() | |
1000 | * @see #keySet() | |
1001 | * @see Map | |
1002 | */ | |
1003 | 0 | public Enumeration keys() { |
1004 | 0 | return new KeyIterator(); |
1005 | } | |
1006 | ||
1007 | /** | |
1008 | * Returns an enumeration of the values in this table. Use the Enumeration | |
1009 | * methods on the returned object to fetch the elements sequentially. | |
1010 | * | |
1011 | * @return an enumeration of the values in this table. | |
1012 | * @see java.util.Enumeration | |
1013 | * @see #keys() | |
1014 | * @see #values() | |
1015 | * @see Map | |
1016 | */ | |
1017 | ||
1018 | 0 | public Enumeration elements() { |
1019 | 0 | return new ValueIterator(); |
1020 | } | |
1021 | ||
1022 | /** | |
1023 | * ConcurrentReaderHashMap collision list entry. | |
1024 | */ | |
1025 | ||
1026 | protected static class Entry implements Map.Entry { | |
1027 | ||
1028 | /* | |
1029 | * The use of volatile for value field ensures that we can detect status | |
1030 | * changes without synchronization. The other fields are never changed, | |
1031 | * and are marked as final. | |
1032 | */ | |
1033 | ||
1034 | protected final int hash; | |
1035 | ||
1036 | protected final Object key; | |
1037 | ||
1038 | protected final Entry next; | |
1039 | ||
1040 | protected volatile Object value; | |
1041 | ||
1042 | 22740 | Entry(int hash, Object key, Object value, Entry next) { |
1043 | 22740 | this.hash = hash; |
1044 | 22740 | this.key = key; |
1045 | 22740 | this.next = next; |
1046 | 22740 | this.value = value; |
1047 | } | |
1048 | ||
1049 | // Map.Entry Ops | |
1050 | ||
1051 | 0 | public Object getKey() { |
1052 | 0 | return key; |
1053 | } | |
1054 | ||
1055 | /** | |
1056 | * Get the value. Note: In an entrySet or entrySet.iterator, unless the | |
1057 | * set or iterator is used under synchronization of the table as a whole | |
1058 | * (or you can otherwise guarantee lack of concurrent modification), | |
1059 | * <tt>getValue</tt> <em>might</em> return null, reflecting the fact | |
1060 | * that the entry has been concurrently removed. However, there are no | |
1061 | * assurances that concurrent removals will be reflected using this | |
1062 | * method. | |
1063 | * | |
1064 | * @return the current value, or null if the entry has been detectably | |
1065 | * removed. | |
1066 | */ | |
1067 | 0 | public Object getValue() { |
1068 | 0 | return value; |
1069 | } | |
1070 | ||
1071 | /** | |
1072 | * Set the value of this entry. Note: In an entrySet or | |
1073 | * entrySet.iterator), unless the set or iterator is used under | |
1074 | * synchronization of the table as a whole (or you can otherwise | |
1075 | * guarantee lack of concurrent modification), <tt>setValue</tt> is | |
1076 | * not strictly guaranteed to actually replace the value field obtained | |
1077 | * via the <tt>get</tt> operation of the underlying hash table in | |
1078 | * multithreaded applications. If iterator-wide synchronization is not | |
1079 | * used, and any other concurrent <tt>put</tt> or <tt>remove</tt> | |
1080 | * operations occur, sometimes even to <em>other</em> entries, then | |
1081 | * this change is not guaranteed to be reflected in the hash table. (It | |
1082 | * might, or it might not. There are no assurances either way.) | |
1083 | * | |
1084 | * @param value | |
1085 | * the new value. | |
1086 | * @return the previous value, or null if entry has been detectably | |
1087 | * removed. | |
1088 | * @exception NullPointerException | |
1089 | * if the value is <code>null</code>. | |
1090 | * | |
1091 | */ | |
1092 | ||
1093 | 0 | public Object setValue(Object value) { |
1094 | 0 | if (value == null) |
1095 | 0 | throw new NullPointerException(); |
1096 | 0 | Object oldValue = this.value; |
1097 | 0 | this.value = value; |
1098 | 0 | return oldValue; |
1099 | } | |
1100 | ||
1101 | 0 | public boolean equals(Object o) { |
1102 | 0 | if (!(o instanceof Map.Entry)) |
1103 | 0 | return false; |
1104 | 0 | Map.Entry e = (Map.Entry) o; |
1105 | 0 | return (key.equals(e.getKey()) && value.equals(e.getValue())); |
1106 | } | |
1107 | ||
1108 | 0 | public int hashCode() { |
1109 | 0 | return key.hashCode() ^ value.hashCode(); |
1110 | } | |
1111 | ||
1112 | 0 | public String toString() { |
1113 | 0 | return key + "=" + value; |
1114 | } | |
1115 | ||
1116 | } | |
1117 | ||
1118 | protected class HashIterator implements Iterator, Enumeration { | |
1119 | protected final Entry[] tab; // snapshot of table | |
1120 | ||
1121 | protected int index; // current slot | |
1122 | ||
1123 | protected Entry entry = null; // current node of slot | |
1124 | ||
1125 | protected Object currentKey; // key for current node | |
1126 | ||
1127 | protected Object currentValue; // value for current node | |
1128 | ||
1129 | protected Entry lastReturned = null; // last node returned by next | |
1130 | ||
1131 | 0 | protected HashIterator() { |
1132 | 0 | tab = ConcurrentReaderHashMap.this.getTableForReading(); |
1133 | 0 | index = tab.length - 1; |
1134 | } | |
1135 | ||
1136 | 0 | public boolean hasMoreElements() { |
1137 | 0 | return hasNext(); |
1138 | } | |
1139 | ||
1140 | 0 | public Object nextElement() { |
1141 | 0 | return next(); |
1142 | } | |
1143 | ||
1144 | 0 | public boolean hasNext() { |
1145 | ||
1146 | /* | |
1147 | * currentkey and currentValue are set here to ensure that next() | |
1148 | * returns normally if hasNext() returns true. This avoids surprises | |
1149 | * especially when final element is removed during traversal -- | |
1150 | * instead, we just ignore the removal during current traversal. | |
1151 | */ | |
1152 | ||
1153 | 0 | for (;;) { |
1154 | 0 | if (entry != null) { |
1155 | 0 | Object v = entry.value; |
1156 | 0 | if (v != null) { |
1157 | 0 | currentKey = entry.key; |
1158 | 0 | currentValue = v; |
1159 | 0 | return true; |
1160 | } else | |
1161 | 0 | entry = entry.next; |
1162 | } | |
1163 | ||
1164 | 0 | while (entry == null && index >= 0) |
1165 | 0 | entry = tab[index--]; |
1166 | ||
1167 | 0 | if (entry == null) { |
1168 | 0 | currentKey = currentValue = null; |
1169 | 0 | return false; |
1170 | } | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | 0 | protected Object returnValueOfNext() { |
1175 | 0 | return entry; |
1176 | } | |
1177 | ||
1178 | 0 | public Object next() { |
1179 | 0 | if (currentKey == null && !hasNext()) |
1180 | 0 | throw new NoSuchElementException(); |
1181 | ||
1182 | 0 | Object result = returnValueOfNext(); |
1183 | 0 | lastReturned = entry; |
1184 | 0 | currentKey = currentValue = null; |
1185 | 0 | entry = entry.next; |
1186 | 0 | return result; |
1187 | } | |
1188 | ||
1189 | 0 | public void remove() { |
1190 | 0 | if (lastReturned == null) |
1191 | 0 | throw new IllegalStateException(); |
1192 | 0 | ConcurrentReaderHashMap.this.remove(lastReturned.key); |
1193 | 0 | lastReturned = null; |
1194 | } | |
1195 | ||
1196 | } | |
1197 | ||
1198 | protected class KeyIterator extends HashIterator { | |
1199 | 0 | protected Object returnValueOfNext() { |
1200 | 0 | return currentKey; |
1201 | } | |
1202 | } | |
1203 | ||
1204 | protected class ValueIterator extends HashIterator { | |
1205 | 0 | protected Object returnValueOfNext() { |
1206 | 0 | return currentValue; |
1207 | } | |
1208 | } | |
1209 | ||
1210 | /** | |
1211 | * Save the state of the <tt>ConcurrentReaderHashMap</tt> instance to a | |
1212 | * stream (i.e., serialize it). | |
1213 | * | |
1214 | * @serialData The <i>capacity </i> of the ConcurrentReaderHashMap (the | |
1215 | * length of the bucket array) is emitted (int), followed by the | |
1216 | * <i>size </i> of the ConcurrentReaderHashMap (the number of | |
1217 | * key-value mappings), followed by the key (Object) and value | |
1218 | * (Object) for each key-value mapping represented by the | |
1219 | * ConcurrentReaderHashMap The key-value mappings are emitted in | |
1220 | * no particular order. | |
1221 | */ | |
1222 | ||
1223 | 0 | private synchronized void writeObject(java.io.ObjectOutputStream s) |
1224 | throws IOException { | |
1225 | // Write out the threshold, loadfactor, and any hidden stuff | |
1226 | 0 | s.defaultWriteObject(); |
1227 | ||
1228 | // Write out number of buckets | |
1229 | 0 | s.writeInt(table.length); |
1230 | ||
1231 | // Write out size (number of Mappings) | |
1232 | 0 | s.writeInt(count); |
1233 | ||
1234 | // Write out keys and values (alternating) | |
1235 | 0 | for (int index = table.length - 1; index >= 0; index--) { |
1236 | 0 | Entry entry = table[index]; |
1237 | ||
1238 | 0 | while (entry != null) { |
1239 | 0 | s.writeObject(entry.key); |
1240 | 0 | s.writeObject(entry.value); |
1241 | 0 | entry = entry.next; |
1242 | } | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | /** | |
1247 | * Reconstitute the <tt>ConcurrentReaderHashMap</tt> instance from a | |
1248 | * stream (i.e., deserialize it). | |
1249 | */ | |
1250 | 0 | private synchronized void readObject(java.io.ObjectInputStream s) |
1251 | throws IOException, ClassNotFoundException { | |
1252 | // Read in the threshold, loadfactor, and any hidden stuff | |
1253 | 0 | s.defaultReadObject(); |
1254 | ||
1255 | // Read in number of buckets and allocate the bucket array; | |
1256 | 0 | int numBuckets = s.readInt(); |
1257 | 0 | table = new Entry[numBuckets]; |
1258 | ||
1259 | // Read in size (number of Mappings) | |
1260 | 0 | int size = s.readInt(); |
1261 | ||
1262 | // Read the keys and values, and put the mappings in the table | |
1263 | 0 | for (int i = 0; i < size; i++) { |
1264 | 0 | Object key = s.readObject(); |
1265 | 0 | Object value = s.readObject(); |
1266 | 0 | put(key, value); |
1267 | } | |
1268 | } | |
1269 | ||
1270 | /** | |
1271 | * Return the number of slots in this table | |
1272 | */ | |
1273 | ||
1274 | 0 | public synchronized int capacity() { |
1275 | 0 | return table.length; |
1276 | } | |
1277 | ||
1278 | /** | |
1279 | * Return the load factor | |
1280 | */ | |
1281 | 0 | public float loadFactor() { |
1282 | 0 | return loadFactor; |
1283 | } | |
1284 | } |
|