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