ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentReaderHashMap.java
Revision: 1.4
Committed: Fri Jun 6 16:53:04 2003 UTC (20 years, 11 months ago) by dl
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +0 -0 lines
State: FILE REMOVED
Log Message:
Minor doc updates; FairReentrantLock serialize now

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