ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentReaderHashMap.java
Revision: 1.2
Committed: Thu May 29 13:49:24 2003 UTC (21 years ago) by dl
Branch: MAIN
CVS Tags: JSR166_PRERELEASE_0_1
Changes since 1.1: +1 -1 lines
Log Message:
Please the new generics compiler

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain. Use, modify, and
4 * redistribute this code in any way without acknowledgement.
5 */
6
7 package java.util.concurrent;
8 import java.util.*;
9 import java.io.*;
10
11 /**
12 * A version of Hashtable that supports mostly-concurrent reading, but
13 * exclusive writing. Because reads are not limited to periods
14 * without writes, a concurrent reader policy is weaker than a classic
15 * reader/writer policy, but is generally faster and allows more
16 * concurrency. This class is a good choice especially for tables that
17 * are mainly created by one thread during the start-up phase of a
18 * program, and from then on, are mainly read (with perhaps occasional
19 * additions or removals) in many threads. If you also need concurrency
20 * among writes, consider instead using ConcurrentHashMap.
21 * <p>
22 *
23 * Successful retrievals using get(key) and containsKey(key) usually
24 * run without locking. Unsuccessful ones (i.e., when the key is not
25 * present) do involve brief synchronization (locking). Also, the
26 * size and isEmpty methods are always synchronized.
27 *
28 * <p> Because retrieval operations can ordinarily overlap with
29 * writing operations (i.e., put, remove, and their derivatives),
30 * retrievals can only be guaranteed to return the results of the most
31 * recently <em>completed</em> operations holding upon their
32 * onset. Retrieval operations may or may not return results
33 * reflecting in-progress writing operations. However, the retrieval
34 * operations do always return consistent results -- either those
35 * holding before any single modification or after it, but never a
36 * nonsense result. For aggregate operations such as putAll and
37 * clear, concurrent reads may reflect insertion or removal of only
38 * some entries. In those rare contexts in which you use a hash table
39 * to synchronize operations across threads (for example, to prevent
40 * reads until after clears), you should either encase operations
41 * in synchronized blocks, or instead use java.util.Hashtable.
42 *
43 */
44
45 public class ConcurrentReaderHashMap<K,V> extends Dictionary<K,V>
46 implements ConcurrentMap<K,V>, Cloneable, Serializable {
47
48 /*
49 * This implementation is thread-safe, but not heavily
50 * synchronized. The basic strategy is to ensure that the hash
51 * table and its lists are ALWAYS kept in a consistent state, so
52 * can be read without locking. Next fields of nodes are
53 * immutable (final). All list additions are performed at the
54 * front of each bin. This makes it easy to check changes, and
55 * also fast to traverse. When nodes would otherwise be changed,
56 * new nodes are created to replace them. This works well for hash
57 * tables since the bin lists tend to be short. (The average
58 * length is less than two for the default load factor threshold.)
59 *
60 * Read operations can thus proceed without locking, but rely on a
61 * memory barrier to ensure that COMPLETED write operations
62 * performed by other threads are noticed. Conveniently, the
63 * "count" field, tracking the number of elements, can also serve
64 * as the volatile variable providing proper read/write
65 * barriers. This is convenient because this field needs to be
66 * read in many read operations anyway. The use of volatiles for
67 * this purpose is only guaranteed to work in accord with normal
68 * expectations in multithreaded environments when run on JVMs
69 * conforming to the clarified JSR133 memory model specification.
70 * This true for hotspot as of release 1.4.
71 *
72 * Implementors note. The basic rules for all this are:
73 * - All unsynchronized read operations must first read
74 * the "count" field, and generally, should not look at table if 0.
75 *
76 * - All synchronized write operations should write to
77 * the "count" field after updating. The operations may not
78 * take any action that could even momentarily cause
79 * a concurrent read operation to see inconsistent
80 * data. This is made easier by the nature of the read
81 * operations in ConcurrentReaderHashMap. For example, no operation
82 * can reveal that the table has grown but the threshold
83 * has not yet been updated, so there are no atomicity
84 * requirements for this with respect to reads.
85 *
86 * As a guide, all critical volatile reads and writes are marked
87 * in the code as comments.
88 */
89
90 /** use serialVersionUID from JDK 1.0.2 for interoperability */
91 private static final long serialVersionUID = 1421746759512286392L;
92
93 /**
94 * The default initial number of table slots for this table (32).
95 * Used when not otherwise specified in constructor.
96 */
97 static int DEFAULT_INITIAL_CAPACITY = 16;
98
99 /**
100 * The maximum capacity, used if a higher value is implicitly
101 * specified by either of the constructors with arguments. MUST
102 * be a power of two <= 1<<30.
103 */
104 static final int MAXIMUM_CAPACITY = 1 << 30;
105
106 /**
107 * The default load factor for this table. Used when not
108 * otherwise specified in constructor.
109 */
110
111 static final float DEFAULT_LOAD_FACTOR = 0.75f;
112
113 /**
114 * The total number of mappings in the hash table.
115 * Also serves as the read-barrier variable.
116 */
117 private transient volatile int count;
118
119 /**
120 * The hash table data.
121 */
122 private transient HashEntry<K,V>[] table;
123
124 /**
125 * The load factor for the hash table. This is also used as a
126 * recursion flag in method hashCode. (Sorry for the sleaze but
127 * this maintains 1.1 compatibility.)
128 *
129 * @serial
130 */
131 private float loadFactor;
132
133 /**
134 * The table is rehashed when its size exceeds this threshold.
135 * (The value of this field is always (int)(capacity *
136 * loadFactor).)
137 *
138 * @serial
139 */
140 private int threshold;
141
142 /**
143 * The number of times this map has been structurally modified
144 * Structural modifications are those that change the number of
145 * mappings in the map or otherwise modify its internal structure
146 * (e.g., rehash). This field is used to make iterators on
147 * Collection-views of the map fail-fast. (See
148 * ConcurrentModificationException).
149 */
150 private transient int modCount;
151
152 // internal utilities
153
154 /**
155 * Return a hash code for non-null Object x.
156 */
157 private static int hash(Object x) {
158 int h = x.hashCode();
159 h += ~(h << 9);
160 h ^= (h >>> 14);
161 h += (h << 4);
162 h ^= (h >>> 10);
163 return h;
164 }
165
166 /**
167 * Check for equality of non-null references x and y.
168 **/
169 private static boolean eq(Object x, Object y) {
170 return x == y || x.equals(y);
171 }
172
173 /**
174 * Return index for hash code h.
175 */
176 private static int indexFor(int h, int length) {
177 return h & (length-1);
178 }
179
180 /**
181 * Set table to new HashEntry array.
182 * Call only while holding lock or in constructor.
183 **/
184 private void setTable(HashEntry<K,V>[] newTable) {
185 table = newTable;
186 threshold = (int)(newTable.length * loadFactor);
187 count = count; // write-volatile
188 }
189
190 /**
191 * Constructs a new, empty map with a default initial capacity
192 * and load factor.
193 */
194 public ConcurrentReaderHashMap() {
195 loadFactor = DEFAULT_LOAD_FACTOR;
196 setTable(new HashEntry[DEFAULT_INITIAL_CAPACITY]);
197 }
198
199 /**
200 * Constructs a new, empty map with the specified initial
201 * capacity and the specified load factor.
202 *
203 * @param initialCapacity the initial capacity
204 * The actual initial capacity is rounded to the nearest power of two.
205 * @param loadFactor the load factor of the ConcurrentReaderHashMap
206 * @throws IllegalArgumentException if the initial capacity is less
207 * than zero, or if the load factor is nonpositive.
208 */
209 public ConcurrentReaderHashMap(int initialCapacity, float loadFactor) {
210 if (initialCapacity < 0)
211 throw new IllegalArgumentException("Illegal initial capacity: " +
212 initialCapacity);
213 if (loadFactor <= 0 || Float.isNaN(loadFactor))
214 throw new IllegalArgumentException("Illegal Load factor: "+
215 loadFactor);
216 this.loadFactor = loadFactor;
217
218 int capacity;
219 if (initialCapacity > MAXIMUM_CAPACITY)
220 capacity = MAXIMUM_CAPACITY;
221 else {
222 capacity = 1;
223 while (capacity < initialCapacity)
224 capacity <<= 1;
225 }
226
227 setTable(new HashEntry[capacity]);
228 }
229
230 /**
231 * Constructs a new, empty map with the specified initial
232 * capacity and default load factor.
233 *
234 * @param initialCapacity the initial capacity of the
235 * ConcurrentReaderHashMap.
236 * The actual initial capacity is rounded to the nearest power of two.
237 * @throws IllegalArgumentException if the initial maximum number
238 * of elements is less
239 * than zero.
240 */
241
242 public ConcurrentReaderHashMap(int initialCapacity) {
243 this(initialCapacity, DEFAULT_LOAD_FACTOR);
244 }
245
246 /**
247 * Constructs a new map with the same mappings as the given map. The
248 * map is created with a default load factor.
249 */
250
251 public ConcurrentReaderHashMap(Map<K,V> t) {
252 this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, 16),
253 DEFAULT_LOAD_FACTOR);
254 putAll(t);
255 }
256
257
258 /**
259 * Returns the number of key-value mappings in this map.
260 *
261 * @return the number of key-value mappings in this map.
262 */
263 public int size() {
264 return count; // read-volatile
265 }
266
267 /**
268 * Returns <tt>true</tt> if this map contains no key-value mappings.
269 *
270 * @return <tt>true</tt> if this map contains no key-value mappings.
271 */
272 public boolean isEmpty() {
273 return count == 0; // read-volatile
274 }
275
276 /**
277 * Returns the value to which the specified key is mapped in this table.
278 *
279 * @param key a key in the table.
280 * @return the value to which the key is mapped in this table;
281 * <code>null</code> if the key is not mapped to any value in
282 * this table.
283 * @exception NullPointerException if the key is
284 * <code>null</code>.
285 * @see #put(Object, Object)
286 */
287 public V get(K key) {
288 int hash = hash(key); // throws NullPointerException if key null
289
290 if (count != 0) { // read-volatile
291 HashEntry<K,V>[] tab = table;
292 int index = indexFor(hash, tab.length);
293 HashEntry<K,V> e = tab[index];
294 while (e != null) {
295 if (e.hash == hash && eq(key, e.key))
296 return e.value;
297 e = e.next;
298 }
299 }
300 return null;
301 }
302
303 /**
304 * Tests if the specified object is a key in this table.
305 *
306 * @param key possible key.
307 * @return <code>true</code> if and only if the specified object
308 * is a key in this table, as determined by the
309 * <tt>equals</tt> method; <code>false</code> otherwise.
310 * @exception NullPointerException if the key is
311 * <code>null</code>.
312 * @see #contains(Object)
313 */
314 public boolean containsKey(Object key) {
315 int hash = hash(key); // throws NullPointerException if key null
316
317 if (count != 0) { // read-volatile
318 HashEntry<K,V>[] tab = table;
319 int index = indexFor(hash, tab.length);
320 HashEntry<K,V> e = tab[index];
321 while (e != null) {
322 if (e.hash == hash && eq(key, e.key))
323 return true;
324 e = e.next;
325 }
326 }
327 return false;
328 }
329
330 /**
331 * Returns <tt>true</tt> if this map maps one or more keys to the
332 * specified value. Note: This method requires a full internal
333 * traversal of the hash table, and so is much slower than
334 * method <tt>containsKey</tt>.
335 *
336 * @param value value whose presence in this map is to be tested.
337 * @return <tt>true</tt> if this map maps one or more keys to the
338 * specified value.
339 * @exception NullPointerException if the value is <code>null</code>.
340 */
341 public boolean containsValue(Object value) {
342 if (value == null)
343 throw new NullPointerException();
344
345 if (count != 0) {
346 HashEntry tab[] = table;
347 int len = tab.length;
348 for (int i = 0 ; i < len; i++)
349 for (HashEntry e = tab[i] ; e != null ; e = e.next)
350 if (value.equals(e.value))
351 return true;
352 }
353 return false;
354 }
355
356
357 /**
358 * Tests if some key maps into the specified value in this table.
359 * This operation is more expensive than the <code>containsKey</code>
360 * method.<p>
361 *
362 * Note that this method is identical in functionality to containsValue,
363 * (which is part of the Map interface in the collections framework).
364 *
365 * @param value a value to search for.
366 * @return <code>true</code> if and only if some key maps to the
367 * <code>value</code> argument in this table as
368 * determined by the <tt>equals</tt> method;
369 * <code>false</code> otherwise.
370 * @exception NullPointerException if the value is <code>null</code>.
371 * @see #containsKey(Object)
372 * @see #containsValue(Object)
373 * @see Map
374 */
375 public boolean contains(Object value) {
376 return containsValue(value);
377 }
378
379 /**
380 * Maps the specified <code>key</code> to the specified
381 * <code>value</code> in this table. Neither the key nor the
382 * value can be <code>null</code>. <p>
383 *
384 * The value can be retrieved by calling the <code>get</code> method
385 * with a key that is equal to the original key.
386 *
387 * @param key the table key.
388 * @param value the value.
389 * @return the previous value of the specified key in this table,
390 * or <code>null</code> if it did not have one.
391 * @exception NullPointerException if the key or value is
392 * <code>null</code>.
393 * @see Object#equals(Object)
394 * @see #get(Object)
395 */
396 public synchronized V put(K key, V value) {
397 if (value == null)
398 throw new NullPointerException();
399 int hash = hash(key);
400 HashEntry<K,V>[] tab = table;
401 int index = indexFor(hash, tab.length);
402 HashEntry<K,V> first = tab[index];
403
404 for (HashEntry<K,V> e = first; e != null; e = e.next) {
405 if (e.hash == hash && eq(key, e.key)) {
406 V oldValue = e.value;
407 e.value = value;
408 count = count; // write-volatile
409 return oldValue;
410 }
411 }
412
413 tab[index] = new HashEntry(hash, key, value, first);
414 modCount++;
415 if (++count > threshold) // write-volatile
416 rehash();
417 return null;
418 }
419
420 public synchronized V putIfAbsent(K key, V value) {
421 if (value == null)
422 throw new NullPointerException();
423 int hash = hash(key);
424 HashEntry<K,V>[] tab = table;
425 int index = indexFor(hash, tab.length);
426 HashEntry<K,V> first = tab[index];
427
428 for (HashEntry<K,V> e = first; e != null; e = e.next) {
429 if (e.hash == hash && eq(key, e.key)) {
430 V oldValue = e.value;
431 count = count; // write-volatile
432 return oldValue;
433 }
434 }
435
436 tab[index] = new HashEntry(hash, key, value, first);
437 modCount++;
438 if (++count > threshold) // write-volatile
439 rehash();
440 return value;
441 }
442
443 /**
444 * Rehashes the contents of this map into a new table
445 * with a larger capacity. This method is called automatically when the
446 * number of keys in this map exceeds the load factor threshold.
447 */
448 private void rehash() {
449 HashEntry<K,V>[] oldTable = table;
450 int oldCapacity = oldTable.length;
451 if (oldCapacity < MAXIMUM_CAPACITY) {
452 HashEntry<K,V>[] newTable = new HashEntry<K,V>[oldCapacity << 1];
453 transfer(oldTable, newTable);
454 setTable(newTable);
455 }
456 }
457
458 /**
459 * Transfer nodes from old table to new table.
460 */
461 private void transfer(HashEntry<K,V>[] oldTable, HashEntry<K,V>[] newTable) {
462 /*
463 * Reclassify nodes in each list to new Map. Because we are
464 * using power-of-two expansion, the elements from each bin
465 * must either stay at same index, or move with a power of two
466 * offset. We eliminate unnecessary node creation by catching
467 * cases where old nodes can be reused because their next
468 * fields won't change. Statistically, at the default
469 * threshhold, only about one-sixth of them need cloning when
470 * a table doubles. The nodes they replace will be garbage
471 * collectable as soon as they are no longer referenced by any
472 * reader thread that may be in the midst of traversing table
473 * right now.
474 */
475
476 int oldCapacity = oldTable.length;
477 int mask = newTable.length - 1;
478 for (int i = 0; i < oldCapacity ; i++) {
479 // We need to guarantee that any existing reads of old Map can
480 // proceed. So we cannot yet null out each bin.
481 HashEntry<K,V> e = oldTable[i];
482
483 if (e != null) {
484 HashEntry<K,V> next = e.next;
485 int idx = e.hash & mask;
486
487 // Single node on list
488 if (next == null)
489 newTable[idx] = e;
490
491 else {
492 // Reuse trailing consecutive sequence at same slot
493 HashEntry<K,V> lastRun = e;
494 int lastIdx = idx;
495 for (HashEntry<K,V> last = next; last != null; last = last.next) {
496 int k = last.hash & mask;
497 if (k != lastIdx) {
498 lastIdx = k;
499 lastRun = last;
500 }
501 }
502 newTable[lastIdx] = lastRun;
503
504 // Clone all remaining nodes
505 for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
506 int k = p.hash & mask;
507 newTable[k] = new HashEntry(p.hash, p.key,
508 p.value, newTable[k]);
509 }
510 }
511 }
512 }
513 }
514
515
516 /**
517 * Copies all of the mappings from the specified map to this one.
518 *
519 * These mappings replace any mappings that this map had for any of the
520 * keys currently in the specified Map.
521 *
522 * @param t Mappings to be stored in this map.
523 */
524
525 public <K1 extends K, V1 extends V> void putAll(Map<K1,V1> t) {
526 int n = t.size();
527 // Expand enough to hold at least n elements without resizing.
528 if (n >= threshold)
529 resizeToFit(n);
530 Iterator<Map.Entry<K1,V1>> it = t.entrySet().iterator();
531 while (it.hasNext()) {
532 Entry<K,V> e = (Entry) it.next();
533 put(e.getKey(), e.getValue());
534 }
535 }
536
537 /**
538 * Resize by enough to fit n elements.
539 */
540 private synchronized void resizeToFit(int n) {
541 int newSize = (int)(n / loadFactor + 1);
542 if (newSize > MAXIMUM_CAPACITY)
543 newSize = MAXIMUM_CAPACITY;
544
545 HashEntry[] oldTable = table;
546 int oldCapacity = oldTable.length;
547 int newCapacity = oldCapacity;
548 while (newCapacity < newSize)
549 newCapacity <<= 1;
550
551 if (newCapacity > oldCapacity) {
552 HashEntry[] newTable = new HashEntry[newCapacity];
553 if (count != 0)
554 transfer(oldTable, newTable);
555 setTable(newTable);
556 }
557 }
558
559
560 /**
561 * Removes the key (and its corresponding value) from this
562 * table. This method does nothing if the key is not in the table.
563 *
564 * @param key the key that needs to be removed.
565 * @return the value to which the key had been mapped in this table,
566 * or <code>null</code> if the key did not have a mapping.
567 * @exception NullPointerException if the key is
568 * <code>null</code>.
569 */
570 public synchronized V remove(Object key) {
571 int hash = hash(key);
572 HashEntry[] tab = table;
573 int index = indexFor(hash, tab.length);
574 HashEntry<K,V> first = tab[index];
575
576 HashEntry<K,V> e = first;
577 while (true) {
578 if (e == null)
579 return null;
580 if (e.hash == hash && eq(key, e.key))
581 break;
582 e = e.next;
583 }
584
585 // All entries following removed node can stay in list, but
586 // all preceeding ones need to be cloned.
587 HashEntry<K,V> newFirst = e.next;
588 for (HashEntry<K,V> p = first; p != e; p = p.next)
589 newFirst = new HashEntry(p.hash, p.key, p.value, newFirst);
590 tab[index] = newFirst;
591
592 modCount++;
593 count--; // write-volatile
594 return e.value;
595 }
596
597
598 /**
599 * Helper method for entrySet.remove
600 */
601 private synchronized boolean findAndRemoveHashEntry(K key,
602 V value) {
603 return key != null && value != null &&
604 value.equals(get(key)) && (remove(key) != null);
605 }
606
607 /**
608 * Removes all mappings from this map.
609 */
610 public synchronized void clear() {
611 modCount++;
612 HashEntry<K,V> tab[] = table;
613 int len = tab.length;
614 for (int i = 0; i < len ; i++)
615 tab[i] = null;
616 count = 0; // write-volatile
617 }
618
619
620 /**
621 * Returns a string representation of this <tt>ConcurrentReaderHashMap</tt> object
622 * in the form of a set of entries, enclosed in braces and separated
623 * by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space). Each
624 * entry is rendered as the key, an equals sign <tt>=</tt>, and the
625 * associated element, where the <tt>toString</tt> method is used to
626 * convert the key and element to strings. <p>Overrides to
627 * <tt>toString</tt> method of <tt>Object</tt>.
628 *
629 * @return a string representation of this hashtable.
630 */
631 public String toString() {
632 if (count == 0) // read-volatile
633 return "{}";
634
635 StringBuffer buf = new StringBuffer();
636 buf.append("{");
637
638 HashEntry<K,V> tab[] = table;
639 int len = tab.length;
640 int k = 0;
641 for (int i = 0 ; i < len; i++) {
642 for (HashEntry<K,V> e = tab[i] ; e != null ; e = e.next) {
643 if (k++ != 0)
644 buf.append(", ");
645 Object key = e.getKey();
646 Object value = e.getValue();
647
648 buf.append((key == this ? "(this Map)" : key) + "=" +
649 (value == this ? "(this Map)": value));
650 }
651 }
652 buf.append("}");
653 return buf.toString();
654 }
655
656 /**
657 * Compares the specified Object with this Map for equality,
658 * as per the definition in the Map interface.
659 *
660 * @return true if the specified Object is equal to this Map.
661 * @see Map#equals(Object)
662 * @since 1.2
663 */
664 public boolean equals(Object o) {
665 if (o == this)
666 return true;
667 if (!(o instanceof Map))
668 return false;
669
670 Map t = (Map) o;
671 if (t.size() != count) // read-volatile
672 return false;
673
674 HashEntry<K,V> tab[] = table;
675 int len = tab.length;
676 for (int i = 0 ; i < len; i++) {
677 for (HashEntry<K,V> e = tab[i] ; e != null ; e = e.next) {
678 Object v = t.get(e.key);
679 if (v == null || !v.equals(e.value))
680 return false;
681 }
682 }
683 return true;
684 }
685
686 /**
687 * Returns the hash code value for this Map as per the definition in the
688 * Map interface.
689 *
690 * @see Map#hashCode()
691 * @since 1.2
692 */
693 public synchronized int hashCode() {
694 /*
695 This implementation maintains compatibility with
696 JDK1.1 to allow computing hashCodes for ConcurrentReaderHashMaps
697 with reference cycles. This requires both synchronization
698 and temporary abuse of the "loadFactor" field to signify
699 that a hashCode is in the midst of being computed so
700 to ignore recursive calls. It is embarassing
701 to use loadFactor in this way, but this tactic permits
702 handling the case without any other field changes.
703
704 Even though hashCodes of cyclic structures can be computed,
705 programs should NOT insert a ConcurrentReaderHashMap into itself. Because
706 its hashCode changes as a result of entering itself, it is
707 normally impossible to retrieve the embedded ConcurrentReaderHashMap using
708 get().
709 */
710 int h = 0;
711 float lf = loadFactor;
712 if (count != 0 && lf > 0) {
713 loadFactor = 0; // zero as recursion flag
714 HashEntry<K,V> tab[] = table;
715 int len = tab.length;
716 for (int i = 0 ; i < len; i++)
717 for (HashEntry<K,V> e = tab[i] ; e != null ; e = e.next)
718 h += e.key.hashCode() ^ e.value.hashCode();
719 loadFactor = lf;
720 }
721 return h;
722 }
723
724
725 /**
726 * Returns a shallow copy of this
727 * <tt>ConcurrentReaderHashMap</tt> instance: the keys and
728 * values themselves are not cloned.
729 *
730 * @return a shallow copy of this map.
731 */
732 public synchronized Object clone() {
733 ConcurrentReaderHashMap result = null;
734 try {
735 result = (ConcurrentReaderHashMap)super.clone();
736 }
737 catch (CloneNotSupportedException e) {
738 // assert false;
739 }
740 result.count = 0;
741 result.keySet = null;
742 result.entrySet = null;
743 result.values = null;
744 result.modCount = 0;
745 result.table = new HashEntry[table.length];
746 result.putAll(this);
747 return result;
748 }
749
750 /**
751 * ConcurrentReaderHashMap collision list entry.
752 */
753 private static class HashEntry<K,V> implements Entry<K,V> {
754 private final K key;
755 private V value;
756 private final int hash;
757 private final HashEntry<K,V> next;
758
759 HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
760 this.value = value;
761 this.hash = hash;
762 this.key = key;
763 this.next = next;
764 }
765
766 public K getKey() {
767 return key;
768 }
769
770 public V getValue() {
771 return value;
772 }
773
774 public V setValue(V newValue) {
775 // We aren't required to, and don't provide any
776 // visibility barriers for setting value.
777 if (newValue == null)
778 throw new NullPointerException();
779 V oldValue = this.value;
780 this.value = newValue;
781 return oldValue;
782 }
783
784 public boolean equals(Object o) {
785 if (!(o instanceof Entry))
786 return false;
787 Entry<K,V> e = (Entry)o;
788 return (key.equals(e.getKey()) && value.equals(e.getValue()));
789 }
790
791 public int hashCode() {
792 return key.hashCode() ^ value.hashCode();
793 }
794
795 public String toString() {
796 return key + "=" + value;
797 }
798 }
799
800 /**
801 * Support for Enumeration interface. These Enumerations take a
802 * snapshot of table, so can never encounter corrupted
803 * representations in multithreaded programs. At worst, they will
804 * report the presence of entries deleted since the enumeration
805 * was constructed, or absence of those inserted.
806 */
807
808 private abstract class HashEnumerator {
809
810
811 HashEntry<K,V> next; // next entry to return
812 final HashEntry[] tab; // snapshot of table
813 int index; // current slot
814
815 HashEnumerator(int size, HashEntry<K,V>[] t) {
816 tab = t;
817 int i = t.length;
818 HashEntry<K,V> n = null;
819 if (size != 0) { // advance to first entry
820 while (i > 0 && (n = tab[--i]) == null)
821 ;
822 }
823 next = n;
824 index = i;
825 }
826
827 public boolean hasMoreElements() {
828 return next != null;
829 }
830
831 HashEntry<K,V> nextHashEntry() {
832 HashEntry<K,V> e = next;
833 if (e == null)
834 throw new NoSuchElementException("ConcurrentReaderHashMap Enumerator");
835
836 HashEntry<K,V> n = e.next;
837 int i = index;
838 while (n == null && i > 0)
839 n = tab[--i];
840 index = i;
841 next = n;
842 return e;
843 }
844 }
845
846 private class KeyEnumerator extends HashEnumerator implements Enumeration<K> {
847 KeyEnumerator(int size, HashEntry<K,V>[] t) { super(size, t); }
848 public K nextElement() {
849 return nextHashEntry().key;
850 }
851 }
852
853 private class ValueEnumerator extends HashEnumerator implements Enumeration<V> {
854 ValueEnumerator(int size, HashEntry<K,V>[] t) { super(size, t); }
855 public V nextElement() {
856 return nextHashEntry().value;
857 }
858 }
859
860 /**
861 * Support for Iterator interface.
862 */
863 private abstract class HashIterator {
864 HashEntry<K,V> next; // next entry to return
865 int expectedModCount; // For fast-fail
866 int index; // current slot
867 HashEntry<K,V> current; // current entry
868
869 HashIterator() {
870 int size = count; // read-volatile
871 HashEntry[] t = table;
872 expectedModCount = modCount;
873 int i = t.length;
874 HashEntry<K,V> n = null;
875 if (size != 0) { // advance to first entry
876 while (i > 0 && (n = t[--i]) == null)
877 ;
878 }
879 next = n;
880 index = i;
881 }
882
883 public boolean hasNext() {
884 return next != null;
885 }
886
887 HashEntry<K,V> nextHashEntry() {
888 int ignore = count; // read-volatile
889 if (modCount != expectedModCount)
890 throw new ConcurrentModificationException();
891 HashEntry<K,V> e = next;
892 if (e == null)
893 throw new NoSuchElementException("ConcurrentReaderHashMap Enumerator");
894
895 HashEntry<K,V> n = e.next;
896 HashEntry[] t = table;
897 int i = index;
898 while (n == null && i > 0)
899 n = t[--i];
900 index = i;
901 next = n;
902 current = e;
903 return e;
904 }
905
906 public void remove() {
907 if (current == null)
908 throw new IllegalStateException("ConcurrentReaderHashMap Enumerator");
909 K k = current.key;
910 current = null;
911 if (ConcurrentReaderHashMap.this.remove(k) == null)
912 throw new ConcurrentModificationException();
913 expectedModCount = modCount;
914 }
915 }
916
917 private class KeyIterator extends HashIterator implements Iterator<K> {
918 KeyIterator() {}
919 public K next() {
920 return nextHashEntry().key;
921 }
922 }
923
924 private class ValueIterator extends HashIterator implements Iterator<V> {
925 ValueIterator() {}
926 public V next() {
927 return nextHashEntry().value;
928 }
929 }
930
931 private class HashEntryIterator extends HashIterator implements Iterator<Entry<K,V>> {
932 HashEntryIterator() {}
933 public Entry<K,V> next() {
934 return nextHashEntry();
935 }
936 }
937
938
939 // Views
940
941 private transient Set<K> keySet = null;
942 private transient Set/*<Entry<K,V>>*/ entrySet = null;
943 private transient Collection<V> values = null;
944
945 /**
946 * Returns a set view of the keys contained in this map. The set is
947 * backed by the map, so changes to the map are reflected in the set, and
948 * vice-versa. The set supports element removal, which removes the
949 * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
950 * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
951 * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
952 * <tt>addAll</tt> operations.
953 *
954 * @return a set view of the keys contained in this map.
955 */
956
957 public Set<K> keySet() {
958 Set<K> ks = keySet;
959 return (ks != null)? ks : (keySet = new KeySet());
960 }
961
962 private class KeySet extends AbstractSet<K> {
963 public Iterator<K> iterator() {
964 return new KeyIterator();
965 }
966 public int size() {
967 return ConcurrentReaderHashMap.this.size();
968 }
969 public boolean contains(Object o) {
970 return ConcurrentReaderHashMap.this.containsKey(o);
971 }
972 public boolean remove(Object o) {
973 return ConcurrentReaderHashMap.this.remove(o) != null;
974 }
975 public void clear() {
976 ConcurrentReaderHashMap.this.clear();
977 }
978 }
979
980 /**
981 * Returns a collection view of the values contained in this map. The
982 * collection is backed by the map, so changes to the map are reflected in
983 * the collection, and vice-versa. The collection supports element
984 * removal, which removes the corresponding mapping from this map, via the
985 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
986 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
987 * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
988 *
989 * @return a collection view of the values contained in this map.
990 */
991
992 public Collection<V> values() {
993 Collection<V> vs = values;
994 return (vs != null)? vs : (values = new Values());
995 }
996
997 private class Values extends AbstractCollection<V> {
998 public Iterator<V> iterator() {
999 return new ValueIterator();
1000 }
1001 public int size() {
1002 return ConcurrentReaderHashMap.this.size();
1003 }
1004 public boolean contains(Object o) {
1005 return ConcurrentReaderHashMap.this.containsValue(o);
1006 }
1007 public void clear() {
1008 ConcurrentReaderHashMap.this.clear();
1009 }
1010 }
1011
1012 /**
1013 * Returns a collection view of the mappings contained in this map. Each
1014 * element in the returned collection is a <tt>Entry</tt>. The
1015 * collection is backed by the map, so changes to the map are reflected in
1016 * the collection, and vice-versa. The collection supports element
1017 * removal, which removes the corresponding mapping from the map, via the
1018 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
1019 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
1020 * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
1021 *
1022 * @return a collection view of the mappings contained in this map.
1023 * @see Entry
1024 */
1025
1026 public Set<Entry<K,V>> entrySet() {
1027 Set<Entry<K,V>> es = entrySet;
1028 return (es != null) ? es : (entrySet = new EntrySet());
1029 }
1030
1031 private class EntrySet extends AbstractSet {
1032 public Iterator<Entry<K,V>> iterator() {
1033 return new HashEntryIterator();
1034 }
1035 public boolean contains(Object o) {
1036 if (!(o instanceof Entry))
1037 return false;
1038 Entry<K,V> entry = (Entry)o;
1039 Object v = ConcurrentReaderHashMap.this.get(entry.getKey());
1040 return v != null && v.equals(entry.getValue());
1041 }
1042 public boolean remove(Object o) {
1043 if (!(o instanceof Entry))
1044 return false;
1045 Entry<K,V> entry = (Entry)o;
1046 return ConcurrentReaderHashMap.this.findAndRemoveHashEntry(entry.getKey(),
1047 entry.getValue());
1048 }
1049 public int size() {
1050 return ConcurrentReaderHashMap.this.size();
1051 }
1052 public void clear() {
1053 ConcurrentReaderHashMap.this.clear();
1054 }
1055 }
1056
1057 /**
1058 * Returns an enumeration of the keys in this table.
1059 *
1060 * @return an enumeration of the keys in this table.
1061 * @see Enumeration
1062 * @see #elements()
1063 * @see #keySet()
1064 * @see Map
1065 */
1066 public Enumeration<K> keys() {
1067 int n = count; // read-volatile
1068 return new KeyEnumerator(n, table);
1069 }
1070
1071 /**
1072 * Returns an enumeration of the values in this table.
1073 * Use the Enumeration methods on the returned object to fetch the elements
1074 * sequentially.
1075 *
1076 * @return an enumeration of the values in this table.
1077 * @see java.util.Enumeration
1078 * @see #keys()
1079 * @see #values()
1080 * @see Map
1081 */
1082
1083 public Enumeration<V> elements() {
1084 int n = count; // read-volatile
1085 return new ValueEnumerator(n, table);
1086 }
1087
1088 /**
1089 * Save the state of the <tt>ConcurrentReaderHashMap</tt>
1090 * instance to a stream (i.e.,
1091 * serialize it).
1092 *
1093 * @serialData The <i>capacity</i> of the
1094 * ConcurrentReaderHashMap (the length of the
1095 * bucket array) is emitted (int), followed by the
1096 * <i>size</i> of the ConcurrentReaderHashMap (the number of key-value
1097 * mappings), followed by the key (Object) and value (Object)
1098 * for each key-value mapping represented by the ConcurrentReaderHashMap
1099 * The key-value mappings are emitted in no particular order.
1100 */
1101
1102 private synchronized void writeObject(java.io.ObjectOutputStream s)
1103 throws IOException {
1104 // Write out the threshold, loadfactor, and any hidden stuff
1105 s.defaultWriteObject();
1106
1107 // Write out number of buckets
1108 s.writeInt(table.length);
1109
1110 // Write out size (number of Mappings)
1111 s.writeInt(count);
1112
1113 // Write out keys and values (alternating)
1114 for (int index = table.length-1; index >= 0; index--) {
1115 HashEntry<K,V> entry = table[index];
1116
1117 while (entry != null) {
1118 s.writeObject(entry.key);
1119 s.writeObject(entry.value);
1120 entry = entry.next;
1121 }
1122 }
1123 }
1124
1125 /**
1126 * Reconstitute the <tt>ConcurrentReaderHashMap</tt>
1127 * instance from a stream (i.e.,
1128 * deserialize it).
1129 */
1130 private synchronized void readObject(java.io.ObjectInputStream s)
1131 throws IOException, ClassNotFoundException {
1132 // Read in the threshold, loadfactor, and any hidden stuff
1133 s.defaultReadObject();
1134
1135 // Read in number of buckets and allocate the bucket array;
1136 int numBuckets = s.readInt();
1137 table = new HashEntry[numBuckets];
1138
1139 // Read in size (number of Mappings)
1140 int size = s.readInt();
1141
1142 // Read the keys and values, and put the mappings in the table
1143 for (int i=0; i<size; i++) {
1144 K key = (K)(s.readObject());
1145 V value = (V)(s.readObject());
1146 put(key, value);
1147 }
1148 }
1149
1150 }