--- /dev/null
+/*
+ File: ConcurrentHashMap
+
+ Written by Doug Lea. Adapted and released, under explicit
+ permission, from JDK1.2 HashMap.java and Hashtable.java which
+ carries the following copyright:
+
+ * Copyright 1997 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+
+ History:
+ Date Who What
+ 26nov2000 dl Created, based on ConcurrentReaderHashMap
+ 12jan2001 dl public release
+ 17nov2001 dl Minor tunings
+ 24oct2003 dl Segment implements Serializable
+*/
+
+package EDU.oswego.cs.dl.util.concurrent;
+
+import java.util.Map;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+
+/**
+ * A version of Hashtable supporting
+ * concurrency for both retrievals and updates:
+ *
+ * <dl>
+ * <dt> Retrievals
+ *
+ * <dd> Retrievals may overlap updates. (This is the same policy as
+ * ConcurrentReaderHashMap.) Successful retrievals using get(key) and
+ * containsKey(key) usually run without locking. Unsuccessful
+ * retrievals (i.e., when the key is not present) do involve brief
+ * synchronization (locking). Because retrieval operations can
+ * ordinarily overlap with update operations (i.e., put, remove, and
+ * their derivatives), retrievals can only be guaranteed to return the
+ * results of the most recently <em>completed</em> operations holding
+ * upon their onset. Retrieval operations may or may not return
+ * results reflecting in-progress writing operations. However, the
+ * retrieval operations do always return consistent results -- either
+ * those holding before any single modification or after it, but never
+ * a nonsense result. For aggregate operations such as putAll and
+ * clear, concurrent reads may reflect insertion or removal of only
+ * some entries.
+ * <p>
+ *
+ * Iterators and Enumerations (i.e., those returned by
+ * keySet().iterator(), entrySet().iterator(), values().iterator(),
+ * keys(), and elements()) return elements reflecting the state of the
+ * hash table at some point at or since the creation of the
+ * iterator/enumeration. They will return at most one instance of
+ * each element (via next()/nextElement()), but might or might not
+ * reflect puts and removes that have been processed since they were
+ * created. They do <em>not</em> throw ConcurrentModificationException.
+ * However, these iterators are designed to be used by only one
+ * thread at a time. Passing an iterator across multiple threads may
+ * lead to unpredictable results if the table is being concurrently
+ * modified. <p>
+ *
+ *
+ * <dt> Updates
+ *
+ * <dd> This class supports a hard-wired preset <em>concurrency
+ * level</em> of 32. This allows a maximum of 32 put and/or remove
+ * operations to proceed concurrently. This level is an upper bound on
+ * concurrency, not a guarantee, since it interacts with how
+ * well-strewn elements are across bins of the table. (The preset
+ * value in part reflects the fact that even on large multiprocessors,
+ * factors other than synchronization tend to be bottlenecks when more
+ * than 32 threads concurrently attempt updates.)
+ * Additionally, operations triggering internal resizing and clearing
+ * do not execute concurrently with any operation.
+ * <p>
+ *
+ * There is <em>NOT</em> any support for locking the entire table to
+ * prevent updates. This makes it imposssible, for example, to
+ * add an element only if it is not already present, since another
+ * thread may be in the process of doing the same thing.
+ * If you need such capabilities, consider instead using the
+ * ConcurrentReaderHashMap class.
+ *
+ * </dl>
+ *
+ * Because of how concurrency control is split up, the
+ * size() and isEmpty() methods require accumulations across 32
+ * control segments, and so might be slightly slower than you expect.
+ * <p>
+ *
+ * This class may be used as a direct replacement for
+ * java.util.Hashtable in any application that does not rely
+ * on the ability to lock the entire table to prevent updates.
+ * As of this writing, it performs much faster than Hashtable in
+ * typical multi-threaded applications with multiple readers and writers.
+ * Like Hashtable but unlike java.util.HashMap,
+ * this class does NOT allow <tt>null</tt> to be used as a key or
+ * value.
+ * <p>
+ *
+ * Implementation note: A slightly
+ * faster implementation of this class will be possible once planned
+ * Java Memory Model revisions are in place.
+ *
+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
+
+ **/
+
+
+public class ConcurrentHashMap
+ extends AbstractMap
+ implements Map, Cloneable, Serializable {
+
+ /*
+ The basic strategy is an optimistic-style scheme based on
+ the guarantee that the hash table and its lists are always
+ kept in a consistent enough state to be read without locking:
+
+ * Read operations first proceed without locking, by traversing the
+ apparently correct list of the apparently correct bin. If an
+ entry is found, but not invalidated (value field null), it is
+ returned. If not found, operations must recheck (after a memory
+ barrier) to make sure they are using both the right list and
+ the right table (which can change under resizes). If
+ invalidated, reads must acquire main update lock to wait out
+ the update, and then re-traverse.
+
+ * All list additions are at the front of each bin, making it easy
+ to check changes, and also fast to traverse. Entry next
+ pointers are never assigned. Remove() builds new nodes when
+ necessary to preserve this.
+
+ * Remove() (also clear()) invalidates removed nodes to alert read
+ operations that they must wait out the full modifications.
+
+ * Locking for puts, removes (and, when necessary gets, etc)
+ is controlled by Segments, each covering a portion of the
+ table. During operations requiring global exclusivity (mainly
+ resize and clear), ALL of these locks are acquired at once.
+ Note that these segments are NOT contiguous -- they are based
+ on the least 5 bits of hashcodes. This ensures that the same
+ segment controls the same slots before and after resizing, which
+ is necessary for supporting concurrent retrievals. This
+ comes at the price of a mismatch of logical vs physical locality,
+ but this seems not to be a performance problem in practice.
+
+ */
+
+ /**
+ * The hash table data.
+ */
+ protected transient Entry[] table;
+
+
+ /**
+ * The number of concurrency control segments.
+ * The value can be at most 32 since ints are used
+ * as bitsets over segments. Emprically, it doesn't
+ * seem to pay to decrease it either, so the value should be at least 32.
+ * In other words, do not redefine this :-)
+ **/
+
+ protected static final int CONCURRENCY_LEVEL = 32;
+
+ /**
+ * Mask value for indexing into segments
+ **/
+
+ protected static final int SEGMENT_MASK = CONCURRENCY_LEVEL - 1;
+
+ /**
+ * Bookkeeping for each concurrency control segment.
+ * Each segment contains a local count of the number of
+ * elements in its region.
+ * However, the main use of a Segment is for its lock.
+ **/
+ protected final static class Segment implements Serializable {
+ /**
+ * The number of elements in this segment's region.
+ * It is always updated within synchronized blocks.
+ **/
+ protected int count;
+
+ /**
+ * Get the count under synch.
+ **/
+ protected synchronized int getCount() { return count; }
+
+ /**
+ * Force a synchronization
+ **/
+ protected synchronized void synch() {}
+ }
+
+ /**
+ * The array of concurrency control segments.
+ **/
+
+ protected final Segment[] segments = new Segment[CONCURRENCY_LEVEL];
+
+
+ /**
+ * The default initial number of table slots for this table (32).
+ * Used when not otherwise specified in constructor.
+ **/
+ public static int DEFAULT_INITIAL_CAPACITY = 32;
+
+
+ /**
+ * The minimum capacity, used if a lower value is implicitly specified
+ * by either of the constructors with arguments.
+ * MUST be a power of two.
+ */
+ private static final int MINIMUM_CAPACITY = 32;
+
+ /**
+ * The maximum capacity, used if a higher value is implicitly specified
+ * by either of the constructors with arguments.
+ * MUST be a power of two <= 1<<30.
+ */
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The default load factor for this table (0.75)
+ * Used when not otherwise specified in constructor.
+ **/
+
+ public static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * The load factor for the hash table.
+ *
+ * @serial
+ */
+ protected final float loadFactor;
+
+ /**
+ * Per-segment resize threshold.
+ *
+ * @serial
+ */
+ protected int threshold;
+
+
+ /**
+ * Number of segments voting for resize. The table is
+ * doubled when 1/4 of the segments reach threshold.
+ * Volatile but updated without synch since this is just a heuristic.
+ **/
+
+ protected transient volatile int votesForResize;
+
+
+ /**
+ * Return the number of set bits in w.
+ * For a derivation of this algorithm, see
+ * "Algorithms and data structures with applications to
+ * graphics and geometry", by Jurg Nievergelt and Klaus Hinrichs,
+ * Prentice Hall, 1993.
+ * See also notes by Torsten Sillke at
+ * http://www.mathematik.uni-bielefeld.de/~sillke/PROBLEMS/bitcount
+ **/
+ protected static int bitcount(int w) {
+ w -= (0xaaaaaaaa & w) >>> 1;
+ w = (w & 0x33333333) + ((w >>> 2) & 0x33333333);
+ w = (w + (w >>> 4)) & 0x0f0f0f0f;
+ w += w >>> 8;
+ w += w >>> 16;
+ return w & 0xff;
+ }
+
+ /**
+ * Returns the appropriate capacity (power of two) for the specified
+ * initial capacity argument.
+ */
+ private int p2capacity(int initialCapacity) {
+ int cap = initialCapacity;
+
+ // Compute the appropriate capacity
+ int result;
+ if (cap > MAXIMUM_CAPACITY || cap < 0) {
+ result = MAXIMUM_CAPACITY;
+ } else {
+ result = MINIMUM_CAPACITY;
+ while (result < cap)
+ result <<= 1;
+ }
+ return result;
+ }
+
+ /**
+ * Return hash code for Object x. Since we are using power-of-two
+ * tables, it is worth the effort to improve hashcode via
+ * the same multiplicative scheme as used in IdentityHashMap.
+ */
+ protected static int hash(Object x) {
+ int h = x.hashCode();
+ // Multiply by 127 (quickly, via shifts), and mix in some high
+ // bits to help guard against bunching of codes that are
+ // consecutive or equally spaced.
+ return ((h << 7) - h + (h >>> 9) + (h >>> 17));
+ }
+
+
+ /**
+ * Check for equality of non-null references x and y.
+ **/
+ protected boolean eq(Object x, Object y) {
+ return x == y || x.equals(y);
+ }
+
+ /** Create table array and set the per-segment threshold **/
+ protected Entry[] newTable(int capacity) {
+ threshold = (int)(capacity * loadFactor / CONCURRENCY_LEVEL) + 1;
+ return new Entry[capacity];
+ }
+
+ /**
+ * Constructs a new, empty map with the specified initial
+ * capacity and the specified load factor.
+ *
+ * @param initialCapacity the initial capacity.
+ * The actual initial capacity is rounded to the nearest power of two.
+ * @param loadFactor the load factor threshold, used to control resizing.
+ * This value is used in an approximate way: When at least
+ * a quarter of the segments of the table reach per-segment threshold, or
+ * one of the segments itself exceeds overall threshold,
+ * the table is doubled.
+ * This will on average cause resizing when the table-wide
+ * load factor is slightly less than the threshold. If you'd like
+ * to avoid resizing, you can set this to a ridiculously large
+ * value.
+ * @throws IllegalArgumentException if the load factor is nonpositive.
+ */
+ public ConcurrentHashMap(int initialCapacity,
+ float loadFactor) {
+ if (!(loadFactor > 0))
+ throw new IllegalArgumentException("Illegal Load factor: "+
+ loadFactor);
+ this.loadFactor = loadFactor;
+ for (int i = 0; i < segments.length; ++i)
+ segments[i] = new Segment();
+ int cap = p2capacity(initialCapacity);
+ table = newTable(cap);
+ }
+
+ /**
+ * Constructs a new, empty map with the specified initial
+ * capacity and default load factor.
+ *
+ * @param initialCapacity the initial capacity of the
+ * ConcurrentHashMap.
+ * @throws IllegalArgumentException if the initial maximum number
+ * of elements is less
+ * than zero.
+ */
+ public ConcurrentHashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Constructs a new, empty map with a default initial capacity
+ * and default load factor.
+ */
+ public ConcurrentHashMap() {
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Constructs a new map with the same mappings as the given map. The
+ * map is created with a capacity of twice the number of mappings in
+ * the given map or 32 (whichever is greater), and a default load factor.
+ */
+ public ConcurrentHashMap(Map t) {
+ this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1,
+ MINIMUM_CAPACITY),
+ DEFAULT_LOAD_FACTOR);
+ putAll(t);
+ }
+
+ /**
+ * Returns the number of key-value mappings in this map.
+ *
+ * @return the number of key-value mappings in this map.
+ */
+ public int size() {
+ int c = 0;
+ for (int i = 0; i < segments.length; ++i)
+ c += segments[i].getCount();
+ return c;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this map contains no key-value mappings.
+ *
+ * @return <tt>true</tt> if this map contains no key-value mappings.
+ */
+ public boolean isEmpty() {
+ for (int i = 0; i < segments.length; ++i)
+ if (segments[i].getCount() != 0)
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Returns the value to which the specified key is mapped in this table.
+ *
+ * @param key a key in the table.
+ * @return the value to which the key is mapped in this table;
+ * <code>null</code> if the key is not mapped to any value in
+ * this table.
+ * @exception NullPointerException if the key is
+ * <code>null</code>.
+ * @see #put(Object, Object)
+ */
+ public Object get(Object key) {
+ int hash = hash(key); // throws null pointer exception if key null
+
+ // Try first without locking...
+ Entry[] tab = table;
+ int index = hash & (tab.length - 1);
+ Entry first = tab[index];
+ Entry e;
+
+ for (e = first; e != null; e = e.next) {
+ if (e.hash == hash && eq(key, e.key)) {
+ Object value = e.value;
+ if (value != null)
+ return value;
+ else
+ break;
+ }
+ }
+
+ // Recheck under synch if key apparently not there or interference
+ Segment seg = segments[hash & SEGMENT_MASK];
+ synchronized(seg) {
+ tab = table;
+ index = hash & (tab.length - 1);
+ Entry newFirst = tab[index];
+ if (e != null || first != newFirst) {
+ for (e = newFirst; e != null; e = e.next) {
+ if (e.hash == hash && eq(key, e.key))
+ return e.value;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key.
+ * @return <code>true</code> if and only if the specified object
+ * is a key in this table, as determined by the
+ * <tt>equals</tt> method; <code>false</code> otherwise.
+ * @exception NullPointerException if the key is
+ * <code>null</code>.
+ * @see #contains(Object)
+ */
+
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+
+ /**
+ * Maps the specified <code>key</code> to the specified
+ * <code>value</code> in this table. Neither the key nor the
+ * value can be <code>null</code>. (Note that this policy is
+ * the same as for java.util.Hashtable, but unlike java.util.HashMap,
+ * which does accept nulls as valid keys and values.)<p>
+ *
+ * The value can be retrieved by calling the <code>get</code> method
+ * with a key that is equal to the original key.
+ *
+ * @param key the table key.
+ * @param value the value.
+ * @return the previous value of the specified key in this table,
+ * or <code>null</code> if it did not have one.
+ * @exception NullPointerException if the key or value is
+ * <code>null</code>.
+ * @see Object#equals(Object)
+ * @see #get(Object)
+ */
+ public Object put(Object key, Object value) {
+ if (value == null)
+ throw new NullPointerException();
+
+ int hash = hash(key);
+ Segment seg = segments[hash & SEGMENT_MASK];
+ int segcount;
+ Entry[] tab;
+ int votes;
+
+ synchronized(seg) {
+ tab = table;
+ int index = hash & (tab.length-1);
+ Entry first = tab[index];
+
+ for (Entry e = first; e != null; e = e.next) {
+ if (e.hash == hash && eq(key, e.key)) {
+ Object oldValue = e.value;
+ e.value = value;
+ return oldValue;
+ }
+ }
+
+ // Add to front of list
+ Entry newEntry = new Entry(hash, key, value, first);
+ tab[index] = newEntry;
+
+ if ((segcount = ++seg.count) < threshold)
+ return null;
+
+ int bit = (1 << (hash & SEGMENT_MASK));
+ votes = votesForResize;
+ if ((votes & bit) == 0)
+ votes = votesForResize |= bit;
+ }
+
+ // Attempt resize if 1/4 segs vote,
+ // or if this seg itself reaches the overall threshold.
+ // (The latter check is just a safeguard to avoid pathological cases.)
+ if (bitcount(votes) >= CONCURRENCY_LEVEL / 4 ||
+ segcount > (threshold * CONCURRENCY_LEVEL))
+ resize(0, tab);
+
+ return null;
+ }
+
+ /**
+ * Gather all locks in order to call rehash, by
+ * recursing within synch blocks for each segment index.
+ * @param index the current segment. initially call value must be 0
+ * @param assumedTab the state of table on first call to resize. If
+ * this changes on any call, the attempt is aborted because the
+ * table has already been resized by another thread.
+ */
+
+ protected void resize(int index, Entry[] assumedTab) {
+ Segment seg = segments[index];
+ synchronized(seg) {
+ if (assumedTab == table) {
+ int next = index+1;
+ if (next < segments.length)
+ resize(next, assumedTab);
+ else
+ rehash();
+ }
+ }
+ }
+
+ /**
+ * Rehashes the contents of this map into a new table
+ * with a larger capacity.
+ */
+ protected void rehash() {
+ votesForResize = 0; // reset
+
+ Entry[] oldTable = table;
+ int oldCapacity = oldTable.length;
+
+ if (oldCapacity >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE; // avoid retriggering
+ return;
+ }
+
+ int newCapacity = oldCapacity << 1;
+ Entry[] newTable = newTable(newCapacity);
+ int mask = newCapacity - 1;
+
+ /*
+ * Reclassify nodes in each list to new Map. Because we are
+ * using power-of-two expansion, the elements from each bin
+ * must either stay at same index, or move to
+ * oldCapacity+index. We also eliminate unnecessary node
+ * creation by catching cases where old nodes can be reused
+ * because their next fields won't change. Statistically, at
+ * the default threshhold, only about one-sixth of them need
+ * cloning. (The nodes they replace will be garbage
+ * collectable as soon as they are no longer referenced by any
+ * reader thread that may be in the midst of traversing table
+ * right now.)
+ */
+
+ for (int i = 0; i < oldCapacity ; i++) {
+ // We need to guarantee that any existing reads of old Map can
+ // proceed. So we cannot yet null out each bin.
+ Entry e = oldTable[i];
+
+ if (e != null) {
+ int idx = e.hash & mask;
+ Entry next = e.next;
+
+ // Single node on list
+ if (next == null)
+ newTable[idx] = e;
+
+ else {
+ // Reuse trailing consecutive sequence of all same bit
+ Entry lastRun = e;
+ int lastIdx = idx;
+ for (Entry last = next; last != null; last = last.next) {
+ int k = last.hash & mask;
+ if (k != lastIdx) {
+ lastIdx = k;
+ lastRun = last;
+ }
+ }
+ newTable[lastIdx] = lastRun;
+
+ // Clone all remaining nodes
+ for (Entry p = e; p != lastRun; p = p.next) {
+ int k = p.hash & mask;
+ newTable[k] = new Entry(p.hash, p.key,
+ p.value, newTable[k]);
+ }
+ }
+ }
+ }
+
+ table = newTable;
+ }
+
+
+ /**
+ * Removes the key (and its corresponding value) from this
+ * table. This method does nothing if the key is not in the table.
+ *
+ * @param key the key that needs to be removed.
+ * @return the value to which the key had been mapped in this table,
+ * or <code>null</code> if the key did not have a mapping.
+ * @exception NullPointerException if the key is
+ * <code>null</code>.
+ */
+ public Object remove(Object key) {
+ return remove(key, null);
+ }
+
+
+ /**
+ * Removes the (key, value) pair from this
+ * table. This method does nothing if the key is not in the table,
+ * or if the key is associated with a different value. This method
+ * is needed by EntrySet.
+ *
+ * @param key the key that needs to be removed.
+ * @param value the associated value. If the value is null,
+ * it means "any value".
+ * @return the value to which the key had been mapped in this table,
+ * or <code>null</code> if the key did not have a mapping.
+ * @exception NullPointerException if the key is
+ * <code>null</code>.
+ */
+
+ protected Object remove(Object key, Object value) {
+ /*
+ Find the entry, then
+ 1. Set value field to null, to force get() to retry
+ 2. Rebuild the list without this entry.
+ All entries following removed node can stay in list, but
+ all preceeding ones need to be cloned. Traversals rely
+ on this strategy to ensure that elements will not be
+ repeated during iteration.
+ */
+
+ int hash = hash(key);
+ Segment seg = segments[hash & SEGMENT_MASK];
+
+ synchronized(seg) {
+ Entry[] tab = table;
+ int index = hash & (tab.length-1);
+ Entry first = tab[index];
+ Entry e = first;
+
+ for (;;) {
+ if (e == null)
+ return null;
+ if (e.hash == hash && eq(key, e.key))
+ break;
+ e = e.next;
+ }
+
+ Object oldValue = e.value;
+ if (value != null && !value.equals(oldValue))
+ return null;
+
+ e.value = null;
+
+ Entry head = e.next;
+ for (Entry p = first; p != e; p = p.next)
+ head = new Entry(p.hash, p.key, p.value, head);
+ tab[index] = head;
+ seg.count--;
+ return oldValue;
+ }
+ }
+
+
+ /**
+ * Returns <tt>true</tt> if this map maps one or more keys to the
+ * specified value. Note: This method requires a full internal
+ * traversal of the hash table, and so is much slower than
+ * method <tt>containsKey</tt>.
+ *
+ * @param value value whose presence in this map is to be tested.
+ * @return <tt>true</tt> if this map maps one or more keys to the
+ * specified value.
+ * @exception NullPointerException if the value is <code>null</code>.
+ */
+ public boolean containsValue(Object value) {
+
+ if (value == null) throw new NullPointerException();
+
+ for (int s = 0; s < segments.length; ++s) {
+ Segment seg = segments[s];
+ Entry[] tab;
+ synchronized(seg) { tab = table; }
+ for (int i = s; i < tab.length; i+= segments.length) {
+ for (Entry e = tab[i]; e != null; e = e.next)
+ if (value.equals(e.value))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests if some key maps into the specified value in this table.
+ * This operation is more expensive than the <code>containsKey</code>
+ * method.<p>
+ *
+ * Note that this method is identical in functionality to containsValue,
+ * (which is part of the Map interface in the collections framework).
+ *
+ * @param value a value to search for.
+ * @return <code>true</code> if and only if some key maps to the
+ * <code>value</code> argument in this table as
+ * determined by the <tt>equals</tt> method;
+ * <code>false</code> otherwise.
+ * @exception NullPointerException if the value is <code>null</code>.
+ * @see #containsKey(Object)
+ * @see #containsValue(Object)
+ * @see Map
+ */
+
+ public boolean contains(Object value) {
+ return containsValue(value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this one.
+ *
+ * These mappings replace any mappings that this map had for any of the
+ * keys currently in the specified Map.
+ *
+ * @param t Mappings to be stored in this map.
+ */
+
+ public void putAll(Map t) {
+ int n = t.size();
+ if (n == 0)
+ return;
+
+ // Expand enough to hold at least n elements without resizing.
+ // We can only resize table by factor of two at a time.
+ // It is faster to rehash with fewer elements, so do it now.
+ for(;;) {
+ Entry[] tab;
+ int max;
+ synchronized(segments[0]) { // must synch on some segment. pick 0.
+ tab = table;
+ max = threshold * CONCURRENCY_LEVEL;
+ }
+ if (n < max)
+ break;
+ resize(0, tab);
+ }
+
+ for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Removes all mappings from this map.
+ */
+
+ public void clear() {
+ // We don't need all locks at once so long as locks
+ // are obtained in low to high order
+ for (int s = 0; s < segments.length; ++s) {
+ Segment seg = segments[s];
+ synchronized(seg) {
+ Entry[] tab = table;
+ for (int i = s; i < tab.length; i+= segments.length) {
+ for (Entry e = tab[i]; e != null; e = e.next)
+ e.value = null;
+ tab[i] = null;
+ seg.count = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a shallow copy of this
+ * <tt>ConcurrentHashMap</tt> instance: the keys and
+ * values themselves are not cloned.
+ *
+ * @return a shallow copy of this map.
+ */
+
+ public Object clone() {
+ // We cannot call super.clone, since it would share final segments array,
+ // and there's no way to reassign finals.
+ return new ConcurrentHashMap(this);
+ }
+
+ // Views
+
+ protected transient Set keySet = null;
+ protected transient Set entrySet = null;
+ protected transient Collection values = null;
+
+ /**
+ * Returns a set view of the keys contained in this map. The set is
+ * backed by the map, so changes to the map are reflected in the set, and
+ * vice-versa. The set supports element removal, which removes the
+ * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
+ * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
+ * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
+ * <tt>addAll</tt> operations.
+ *
+ * @return a set view of the keys contained in this map.
+ */
+
+ public Set keySet() {
+ Set ks = keySet;
+ return (ks != null)? ks : (keySet = new KeySet());
+ }
+
+ private class KeySet extends AbstractSet {
+ public Iterator iterator() {
+ return new KeyIterator();
+ }
+ public int size() {
+ return ConcurrentHashMap.this.size();
+ }
+ public boolean contains(Object o) {
+ return ConcurrentHashMap.this.containsKey(o);
+ }
+ public boolean remove(Object o) {
+ return ConcurrentHashMap.this.remove(o) != null;
+ }
+ public void clear() {
+ ConcurrentHashMap.this.clear();
+ }
+ }
+
+ /**
+ * Returns a collection view of the values contained in this map. The
+ * collection is backed by the map, so changes to the map are reflected in
+ * the collection, and vice-versa. The collection supports element
+ * removal, which removes the corresponding mapping from this map, via the
+ * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
+ * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * @return a collection view of the values contained in this map.
+ */
+
+ public Collection values() {
+ Collection vs = values;
+ return (vs != null)? vs : (values = new Values());
+ }
+
+ private class Values extends AbstractCollection {
+ public Iterator iterator() {
+ return new ValueIterator();
+ }
+ public int size() {
+ return ConcurrentHashMap.this.size();
+ }
+ public boolean contains(Object o) {
+ return ConcurrentHashMap.this.containsValue(o);
+ }
+ public void clear() {
+ ConcurrentHashMap.this.clear();
+ }
+ }
+
+ /**
+ * Returns a collection view of the mappings contained in this map. Each
+ * element in the returned collection is a <tt>Map.Entry</tt>. The
+ * collection is backed by the map, so changes to the map are reflected in
+ * the collection, and vice-versa. The collection supports element
+ * removal, which removes the corresponding mapping from the map, via the
+ * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
+ * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * @return a collection view of the mappings contained in this map.
+ */
+
+ public Set entrySet() {
+ Set es = entrySet;
+ return (es != null) ? es : (entrySet = new EntrySet());
+ }
+
+ private class EntrySet extends AbstractSet {
+ public Iterator iterator() {
+ return new HashIterator();
+ }
+ public boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry entry = (Map.Entry)o;
+ Object v = ConcurrentHashMap.this.get(entry.getKey());
+ return v != null && v.equals(entry.getValue());
+ }
+ public boolean remove(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry)o;
+ return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()) != null;
+ }
+ public int size() {
+ return ConcurrentHashMap.this.size();
+ }
+ public void clear() {
+ ConcurrentHashMap.this.clear();
+ }
+ }
+
+ /**
+ * Returns an enumeration of the keys in this table.
+ *
+ * @return an enumeration of the keys in this table.
+ * @see Enumeration
+ * @see #elements()
+ * @see #keySet()
+ * @see Map
+ */
+ public Enumeration keys() {
+ return new KeyIterator();
+ }
+
+ /**
+ * Returns an enumeration of the values in this table.
+ * Use the Enumeration methods on the returned object to fetch the elements
+ * sequentially.
+ *
+ * @return an enumeration of the values in this table.
+ * @see java.util.Enumeration
+ * @see #keys()
+ * @see #values()
+ * @see Map
+ */
+
+ public Enumeration elements() {
+ return new ValueIterator();
+ }
+
+ /**
+ * ConcurrentHashMap collision list entry.
+ */
+
+ protected static class Entry implements Map.Entry {
+ /*
+ The use of volatile for value field ensures that
+ we can detect status changes without synchronization.
+ The other fields are never changed, and are
+ marked as final.
+ */
+
+ protected final Object key;
+ protected volatile Object value;
+ protected final int hash;
+ protected final Entry next;
+
+ Entry(int hash, Object key, Object value, Entry next) {
+ this.value = value;
+ this.hash = hash;
+ this.key = key;
+ this.next = next;
+ }
+
+ // Map.Entry Ops
+
+ public Object getKey() {
+ return key;
+ }
+
+ /**
+ * Get the value. Note: In an entrySet or entrySet.iterator,
+ * unless you can guarantee lack of concurrent modification,
+ * <tt>getValue</tt> <em>might</em> return null, reflecting the
+ * fact that the entry has been concurrently removed. However,
+ * there are no assurances that concurrent removals will be
+ * reflected using this method.
+ *
+ * @return the current value, or null if the entry has been
+ * detectably removed.
+ **/
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Set the value of this entry. Note: In an entrySet or
+ * entrySet.iterator), unless you can guarantee lack of concurrent
+ * modification, <tt>setValue</tt> is not strictly guaranteed to
+ * actually replace the value field obtained via the <tt>get</tt>
+ * operation of the underlying hash table in multithreaded
+ * applications. If iterator-wide synchronization is not used,
+ * and any other concurrent <tt>put</tt> or <tt>remove</tt>
+ * operations occur, sometimes even to <em>other</em> entries,
+ * then this change is not guaranteed to be reflected in the hash
+ * table. (It might, or it might not. There are no assurances
+ * either way.)
+ *
+ * @param value the new value.
+ * @return the previous value, or null if entry has been detectably
+ * removed.
+ * @exception NullPointerException if the value is <code>null</code>.
+ *
+ **/
+
+ public Object setValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object oldValue = this.value;
+ this.value = value;
+ return oldValue;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry)o;
+ return (key.equals(e.getKey()) && value.equals(e.getValue()));
+ }
+
+ public int hashCode() {
+ return key.hashCode() ^ value.hashCode();
+ }
+
+ public String toString() {
+ return key + "=" + value;
+ }
+
+ }
+
+ protected class HashIterator implements Iterator, Enumeration {
+ protected final Entry[] tab; // snapshot of table
+ protected int index; // current slot
+ protected Entry entry = null; // current node of slot
+ protected Object currentKey; // key for current node
+ protected Object currentValue; // value for current node
+ protected Entry lastReturned = null; // last node returned by next
+
+ protected HashIterator() {
+ // force all segments to synch
+ synchronized(segments[0]) { tab = table; }
+ for (int i = 1; i < segments.length; ++i) segments[i].synch();
+ index = tab.length - 1;
+ }
+
+ public boolean hasMoreElements() { return hasNext(); }
+ public Object nextElement() { return next(); }
+
+ public boolean hasNext() {
+ /*
+ currentkey and currentValue are set here to ensure that next()
+ returns normally if hasNext() returns true. This avoids
+ surprises especially when final element is removed during
+ traversal -- instead, we just ignore the removal during
+ current traversal.
+ */
+
+ for (;;) {
+ if (entry != null) {
+ Object v = entry.value;
+ if (v != null) {
+ currentKey = entry.key;
+ currentValue = v;
+ return true;
+ }
+ else
+ entry = entry.next;
+ }
+
+ while (entry == null && index >= 0)
+ entry = tab[index--];
+
+ if (entry == null) {
+ currentKey = currentValue = null;
+ return false;
+ }
+ }
+ }
+
+ protected Object returnValueOfNext() { return entry; }
+
+ public Object next() {
+ if (currentKey == null && !hasNext())
+ throw new NoSuchElementException();
+
+ Object result = returnValueOfNext();
+ lastReturned = entry;
+ currentKey = currentValue = null;
+ entry = entry.next;
+ return result;
+ }
+
+ public void remove() {
+ if (lastReturned == null)
+ throw new IllegalStateException();
+ ConcurrentHashMap.this.remove(lastReturned.key);
+ lastReturned = null;
+ }
+
+ }
+
+ protected class KeyIterator extends HashIterator {
+ protected Object returnValueOfNext() { return currentKey; }
+ }
+
+ protected class ValueIterator extends HashIterator {
+ protected Object returnValueOfNext() { return currentValue; }
+ }
+
+ /**
+ * Save the state of the <tt>ConcurrentHashMap</tt>
+ * instance to a stream (i.e.,
+ * serialize it).
+ *
+ * @serialData
+ * An estimate of the table size, followed by
+ * the key (Object) and value (Object)
+ * for each key-value mapping, followed by a null pair.
+ * The key-value mappings are emitted in no particular order.
+ */
+
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws IOException {
+ // Write out the loadfactor, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out capacity estimate. It is OK if this
+ // changes during the write, since it is only used by
+ // readObject to set initial capacity, to avoid needless resizings.
+
+ int cap;
+ synchronized(segments[0]) { cap = table.length; }
+ s.writeInt(cap);
+
+ // Write out keys and values (alternating)
+ for (int k = 0; k < segments.length; ++k) {
+ Segment seg = segments[k];
+ Entry[] tab;
+ synchronized(seg) { tab = table; }
+ for (int i = k; i < tab.length; i+= segments.length) {
+ for (Entry e = tab[i]; e != null; e = e.next) {
+ s.writeObject(e.key);
+ s.writeObject(e.value);
+ }
+ }
+ }
+
+ s.writeObject(null);
+ s.writeObject(null);
+ }
+
+ /**
+ * Reconstitute the <tt>ConcurrentHashMap</tt>
+ * instance from a stream (i.e.,
+ * deserialize it).
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ // Read in the threshold, loadfactor, and any hidden stuff
+ s.defaultReadObject();
+
+ int cap = s.readInt();
+ table = newTable(cap);
+ for (int i = 0; i < segments.length; ++i)
+ segments[i] = new Segment();
+
+
+ // Read the keys and values, and put the mappings in the table
+ for (;;) {
+ Object key = s.readObject();
+ Object value = s.readObject();
+ if (key == null)
+ break;
+ put(key, value);
+ }
+ }
+}