KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > dom4j > tree > ConcurrentReaderHashMap


1 /*
2  File: ConcurrentReaderHashMap
3
4  Written by Doug Lea. Adapted and released, under explicit
5  permission, from JDK1.2 HashMap.java and Hashtable.java which
6  carries the following copyright:
7
8  * Copyright 1997 by Sun Microsystems, Inc.,
9  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
10  * All rights reserved.
11  *
12  * This software is the confidential and proprietary information
13  * of Sun Microsystems, Inc. ("Confidential Information"). You
14  * shall not disclose such Confidential Information and shall use
15  * it only in accordance with the terms of the license agreement
16  * you entered into with Sun.
17
18  History:
19  Date Who What
20  28oct1999 dl Created
21  14dec1999 dl jmm snapshot
22  19apr2000 dl use barrierLock
23  12jan2001 dl public release
24  17nov2001 dl Minor tunings
25  20may2002 dl BarrierLock can now be serialized.
26  09dec2002 dl Fix interference checks.
27  */

28
29 package org.dom4j.tree;
30
31 import java.io.IOException JavaDoc;
32 import java.io.Serializable JavaDoc;
33 import java.util.AbstractCollection JavaDoc;
34 import java.util.AbstractMap JavaDoc;
35 import java.util.AbstractSet JavaDoc;
36 import java.util.Collection JavaDoc;
37 import java.util.Enumeration JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.NoSuchElementException JavaDoc;
41 import java.util.Set JavaDoc;
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 JavaDoc implements Map JavaDoc, Cloneable JavaDoc,
156         Serializable JavaDoc {
157
158     /*
159      * The basic strategy is an optimistic-style scheme based on the guarantee
160      * that the hash table and its lists are always kept in a consistent enough
161      * state to be read without locking:
162      *
163      * Read operations first proceed without locking, by traversing the
164      * apparently correct list of the apparently correct bin. If an entry is
165      * found, but not invalidated (value field null), it is returned. If not
166      * found, operations must recheck (after a memory barrier) to make sure they
167      * are using both the right list and the right table (which can change under
168      * resizes). If invalidated, reads must acquire main update lock to wait out
169      * the update, and then re-traverse.
170      *
171      * All list additions are at the front of each bin, making it easy to check
172      * changes, and also fast to traverse. Entry next pointers are never
173      * assigned. Remove() builds new nodes when necessary to preserve this.
174      *
175      * Remove() (also clear()) invalidates removed nodes to alert read
176      * operations that they must wait out the full modifications.
177      *
178      */

179
180     /** A Serializable class for barrier lock * */
181     protected static class BarrierLock implements java.io.Serializable JavaDoc {
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 JavaDoc 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 JavaDoc 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         // Compute the appropriate capacity
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 JavaDoc x) {
291         int h = x.hashCode();
292         // Multiply by 127 (quickly, via shifts), and mix in some high
293
// bits to help guard against bunching of codes that are
294
// consecutive or equally spaced.
295
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 JavaDoc x, Object JavaDoc 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 JavaDoc("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 JavaDoc 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 JavaDoc get(Object JavaDoc key) {
400
401         // throw null pointer exception if key null
402
int hash = hash(key);
403
404         /*
405          * Start off at the apparently correct bin. If entry is found, we need
406          * to check after a barrier anyway. If not found, we need a barrier to
407          * check if we are actually in right bin. So either way, we encounter
408          * only one barrier unless we need to retry. And we only need to fully
409          * synchronize if there have been concurrent modifications.
410          */

411
412         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                 // If key apparently not there, check to
421
// make sure this was a valid read
422

423                 Entry[] reread = getTableForReading();
424                 if (tab == reread && first == tab[index])
425                     return null;
426                 else {
427                     // Wrong list -- must restart traversal at new first
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 JavaDoc value = e.value;
436                 if (value != null)
437                     return value;
438
439                 // Entry was invalidated during deletion. But it could
440
// have been re-inserted, so we must retraverse.
441
// To avoid useless contention, get lock to wait out
442
// modifications
443
// before retraversing.
444

445                 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 JavaDoc 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 JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
493         if (value == null)
494             throw new NullPointerException JavaDoc();
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                     // make sure we are adding to correct list
510
if (first == tab[index]) {
511                         // Add to front of list
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 JavaDoc oldValue = e.value;
522                     if (first == tab[index] && oldValue != null) {
523                         e.value = value;
524                         return oldValue;
525                     }
526                 }
527             }
528
529             // retry if wrong list or lost race against concurrent remove
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 JavaDoc sput(Object JavaDoc key, Object JavaDoc 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 JavaDoc 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; // avoid retriggering
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          * Reclassify nodes in each list to new Map. Because we are using
583          * power-of-two expansion, the elements from each bin must either stay
584          * at same index, or move to oldCapacity+index. We also eliminate
585          * unnecessary node creation by catching cases where old nodes can be
586          * reused because their next fields won't change. Statistically, at the
587          * default threshhold, only about one-sixth of them need cloning. (The
588          * nodes they replace will be garbage collectable as soon as they are no
589          * longer referenced by any reader thread that may be in the midst of
590          * traversing table right now.)
591          */

592
593         for (int i = 0; i < oldCapacity; i++) {
594             // We need to guarantee that any existing reads of old Map can
595
// proceed. So we cannot yet null out each bin.
596
Entry e = oldTable[i];
597
598             if (e != null) {
599                 int idx = e.hash & mask;
600                 Entry next = e.next;
601
602                 // Single node on list
603
if (next == null)
604                     newTable[idx] = e;
605
606                 else {
607                     // Reuse trailing consecutive sequence of all same bit
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                     // Clone all remaining nodes
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 JavaDoc remove(Object JavaDoc key) {
646         /*
647          * Find the entry, then 1. Set value field to null, to force get() to
648          * retry 2. Rebuild the list without this entry. All entries following
649          * removed node can stay in list, but all preceeding ones need to be
650          * cloned. Traversals rely on this strategy to ensure that elements will
651          * not be repeated during iteration.
652          */

653
654         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 JavaDoc 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             // Wrong list or interference
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 JavaDoc sremove(Object JavaDoc 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 JavaDoc 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 JavaDoc value) {
732         if (value == null)
733             throw new NullPointerException JavaDoc();
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 JavaDoc 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 JavaDoc t) {
781         int n = t.size();
782         if (n == 0)
783             return;
784
785         // Expand enough to hold at least n elements without resizing.
786
// We can only resize table by factor of two at a time.
787
// It is faster to rehash with fewer elements, so do it now.
788
while (n >= threshold)
789             rehash();
790
791         for (Iterator JavaDoc it = t.entrySet().iterator(); it.hasNext();) {
792             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
793             Object JavaDoc key = entry.getKey();
794             Object JavaDoc 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             // must invalidate all to force concurrent get's to wait and then
807
// retry
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 JavaDoc 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 JavaDoc e) {
845             // this shouldn't happen, since we are Cloneable
846
throw new InternalError JavaDoc();
847         }
848     }
849
850     // Views
851

852     protected transient Set JavaDoc keySet = null;
853
854     protected transient Set JavaDoc entrySet = null;
855
856     protected transient Collection JavaDoc 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 JavaDoc keySet() {
871         Set JavaDoc ks = keySet;
872         return (ks != null) ? ks : (keySet = new KeySet());
873     }
874
875     private class KeySet extends AbstractSet JavaDoc {
876         public Iterator JavaDoc iterator() {
877             return new KeyIterator();
878         }
879
880         public int size() {
881             return ConcurrentReaderHashMap.this.size();
882         }
883
884         public boolean contains(Object JavaDoc o) {
885             return ConcurrentReaderHashMap.this.containsKey(o);
886         }
887
888         public boolean remove(Object JavaDoc 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 JavaDoc values() {
911         Collection JavaDoc vs = values;
912         return (vs != null) ? vs : (values = new Values());
913     }
914
915     private class Values extends AbstractCollection JavaDoc {
916         public Iterator JavaDoc iterator() {
917             return new ValueIterator();
918         }
919
920         public int size() {
921             return ConcurrentReaderHashMap.this.size();
922         }
923
924         public boolean contains(Object JavaDoc 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 JavaDoc entrySet() {
948         Set JavaDoc es = entrySet;
949         return (es != null) ? es : (entrySet = new EntrySet());
950     }
951
952     private class EntrySet extends AbstractSet JavaDoc {
953         public Iterator JavaDoc iterator() {
954             return new HashIterator();
955         }
956
957         public boolean contains(Object JavaDoc o) {
958             if (!(o instanceof Map.Entry JavaDoc))
959                 return false;
960             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) o;
961             Object JavaDoc v = ConcurrentReaderHashMap.this.get(entry.getKey());
962             return v != null && v.equals(entry.getValue());
963         }
964
965         public boolean remove(Object JavaDoc o) {
966             if (!(o instanceof Map.Entry JavaDoc))
967                 return false;
968             return ConcurrentReaderHashMap.this
969                     .findAndRemoveEntry((Map.Entry JavaDoc) 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 JavaDoc entry) {
985         Object JavaDoc key = entry.getKey();
986         Object JavaDoc 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 JavaDoc 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 JavaDoc elements() {
1019        return new ValueIterator();
1020    }
1021
1022    /**
1023     * ConcurrentReaderHashMap collision list entry.
1024     */

1025
1026    protected static class Entry implements Map.Entry JavaDoc {
1027
1028        /*
1029         * The use of volatile for value field ensures that we can detect status
1030         * changes without synchronization. The other fields are never changed,
1031         * and are marked as final.
1032         */

1033
1034        protected final int hash;
1035
1036        protected final Object JavaDoc key;
1037
1038        protected final Entry next;
1039
1040        protected volatile Object JavaDoc value;
1041
1042        Entry(int hash, Object JavaDoc key, Object JavaDoc value, Entry next) {
1043            this.hash = hash;
1044            this.key = key;
1045            this.next = next;
1046            this.value = value;
1047        }
1048
1049        // Map.Entry Ops
1050

1051        public Object JavaDoc 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 JavaDoc 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 JavaDoc setValue(Object JavaDoc value) {
1094            if (value == null)
1095                throw new NullPointerException JavaDoc();
1096            Object JavaDoc oldValue = this.value;
1097            this.value = value;
1098            return oldValue;
1099        }
1100
1101        public boolean equals(Object JavaDoc o) {
1102            if (!(o instanceof Map.Entry JavaDoc))
1103                return false;
1104            Map.Entry JavaDoc e = (Map.Entry JavaDoc) 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 JavaDoc toString() {
1113            return key + "=" + value;
1114        }
1115
1116    }
1117
1118    protected class HashIterator implements Iterator JavaDoc, Enumeration JavaDoc {
1119        protected final Entry[] tab; // snapshot of table
1120

1121        protected int index; // current slot
1122

1123        protected Entry entry = null; // current node of slot
1124

1125        protected Object JavaDoc currentKey; // key for current node
1126

1127        protected Object JavaDoc currentValue; // value for current node
1128

1129        protected Entry lastReturned = null; // last node returned by next
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 JavaDoc nextElement() {
1141            return next();
1142        }
1143
1144        public boolean hasNext() {
1145
1146            /*
1147             * currentkey and currentValue are set here to ensure that next()
1148             * returns normally if hasNext() returns true. This avoids surprises
1149             * especially when final element is removed during traversal --
1150             * instead, we just ignore the removal during current traversal.
1151             */

1152
1153            for (;;) {
1154                if (entry != null) {
1155                    Object JavaDoc 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 JavaDoc returnValueOfNext() {
1175            return entry;
1176        }
1177
1178        public Object JavaDoc next() {
1179            if (currentKey == null && !hasNext())
1180                throw new NoSuchElementException JavaDoc();
1181
1182            Object JavaDoc 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 JavaDoc();
1192            ConcurrentReaderHashMap.this.remove(lastReturned.key);
1193            lastReturned = null;
1194        }
1195
1196    }
1197
1198    protected class KeyIterator extends HashIterator {
1199        protected Object JavaDoc returnValueOfNext() {
1200            return currentKey;
1201        }
1202    }
1203
1204    protected class ValueIterator extends HashIterator {
1205        protected Object JavaDoc 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 JavaDoc s)
1224            throws IOException JavaDoc {
1225        // Write out the threshold, loadfactor, and any hidden stuff
1226
s.defaultWriteObject();
1227
1228        // Write out number of buckets
1229
s.writeInt(table.length);
1230
1231        // Write out size (number of Mappings)
1232
s.writeInt(count);
1233
1234        // Write out keys and values (alternating)
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 JavaDoc s)
1251            throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1252        // Read in the threshold, loadfactor, and any hidden stuff
1253
s.defaultReadObject();
1254
1255        // Read in number of buckets and allocate the bucket array;
1256
int numBuckets = s.readInt();
1257        table = new Entry[numBuckets];
1258
1259        // Read in size (number of Mappings)
1260
int size = s.readInt();
1261
1262        // Read the keys and values, and put the mappings in the table
1263
for (int i = 0; i < size; i++) {
1264            Object JavaDoc key = s.readObject();
1265            Object JavaDoc 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}
1285
Popular Tags