Clover coverage report - dom4j - 1.6.1 Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00 FRAMES NO FRAMES |
file stats: | LOC: | 1.284 |  | Methods: | 63
| NCLOC: | 535 |  | Classes: | 9
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 |  | }