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