summaryrefslogtreecommitdiff
path: root/external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java
diff options
context:
space:
mode:
Diffstat (limited to 'external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java')
-rw-r--r--external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java3114
1 files changed, 0 insertions, 3114 deletions
diff --git a/external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java b/external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java
deleted file mode 100644
index 52cd17a52..000000000
--- a/external/jsr166/java/util/concurrent/ConcurrentSkipListMap.java
+++ /dev/null
@@ -1,3114 +0,0 @@
-/*
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
- */
-
-package java.util.concurrent;
-import java.util.*;
-import java.util.concurrent.atomic.*;
-
-/**
- * A scalable concurrent {@link ConcurrentNavigableMap} implementation.
- * The map is sorted according to the {@linkplain Comparable natural
- * ordering} of its keys, or by a {@link Comparator} provided at map
- * creation time, depending on which constructor is used.
- *
- * <p>This class implements a concurrent variant of <a
- * href="http://www.cs.umd.edu/~pugh/">SkipLists</a> providing
- * expected average <i>log(n)</i> time cost for the
- * <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and
- * <tt>remove</tt> operations and their variants. Insertion, removal,
- * update, and access operations safely execute concurrently by
- * multiple threads. Iterators are <i>weakly consistent</i>, returning
- * elements reflecting the state of the map at some point at or since
- * the creation of the iterator. They do <em>not</em> throw {@link
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations. Ascending key ordered views and their iterators
- * are faster than descending ones.
- *
- * <p>All <tt>Map.Entry</tt> pairs returned by methods in this class
- * and its views represent snapshots of mappings at the time they were
- * produced. They do <em>not</em> support the <tt>Entry.setValue</tt>
- * method. (Note however that it is possible to change mappings in the
- * associated map using <tt>put</tt>, <tt>putIfAbsent</tt>, or
- * <tt>replace</tt>, depending on exactly which effect you need.)
- *
- * <p>Beware that, unlike in most collections, the <tt>size</tt>
- * method is <em>not</em> a constant-time operation. Because of the
- * asynchronous nature of these maps, determining the current number
- * of elements requires a traversal of the elements. Additionally,
- * the bulk operations <tt>putAll</tt>, <tt>equals</tt>, and
- * <tt>clear</tt> are <em>not</em> guaranteed to be performed
- * atomically. For example, an iterator operating concurrently with a
- * <tt>putAll</tt> operation might view only some of the added
- * elements.
- *
- * <p>This class and its views and iterators implement all of the
- * <em>optional</em> methods of the {@link Map} and {@link Iterator}
- * interfaces. Like most other concurrent collections, this class does
- * <em>not</em> permit the use of <tt>null</tt> keys or values because some
- * null return values cannot be reliably distinguished from the absence of
- * elements.
- *
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
- * @author Doug Lea
- * @param <K> the type of keys maintained by this map
- * @param <V> the type of mapped values
- * @since 1.6
- */
-public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
- implements ConcurrentNavigableMap<K,V>,
- Cloneable,
- java.io.Serializable {
- /*
- * This class implements a tree-like two-dimensionally linked skip
- * list in which the index levels are represented in separate
- * nodes from the base nodes holding data. There are two reasons
- * for taking this approach instead of the usual array-based
- * structure: 1) Array based implementations seem to encounter
- * more complexity and overhead 2) We can use cheaper algorithms
- * for the heavily-traversed index lists than can be used for the
- * base lists. Here's a picture of some of the basics for a
- * possible list with 2 levels of index:
- *
- * Head nodes Index nodes
- * +-+ right +-+ +-+
- * |2|---------------->| |--------------------->| |->null
- * +-+ +-+ +-+
- * | down | |
- * v v v
- * +-+ +-+ +-+ +-+ +-+ +-+
- * |1|----------->| |->| |------>| |----------->| |------>| |->null
- * +-+ +-+ +-+ +-+ +-+ +-+
- * v | | | | |
- * Nodes next v v v v v
- * +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
- * | |->|A|->|B|->|C|->|D|->|E|->|F|->|G|->|H|->|I|->|J|->|K|->null
- * +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
- *
- * The base lists use a variant of the HM linked ordered set
- * algorithm. See Tim Harris, "A pragmatic implementation of
- * non-blocking linked lists"
- * http://www.cl.cam.ac.uk/~tlh20/publications.html and Maged
- * Michael "High Performance Dynamic Lock-Free Hash Tables and
- * List-Based Sets"
- * http://www.research.ibm.com/people/m/michael/pubs.htm. The
- * basic idea in these lists is to mark the "next" pointers of
- * deleted nodes when deleting to avoid conflicts with concurrent
- * insertions, and when traversing to keep track of triples
- * (predecessor, node, successor) in order to detect when and how
- * to unlink these deleted nodes.
- *
- * Rather than using mark-bits to mark list deletions (which can
- * be slow and space-intensive using AtomicMarkedReference), nodes
- * use direct CAS'able next pointers. On deletion, instead of
- * marking a pointer, they splice in another node that can be
- * thought of as standing for a marked pointer (indicating this by
- * using otherwise impossible field values). Using plain nodes
- * acts roughly like "boxed" implementations of marked pointers,
- * but uses new nodes only when nodes are deleted, not for every
- * link. This requires less space and supports faster
- * traversal. Even if marked references were better supported by
- * JVMs, traversal using this technique might still be faster
- * because any search need only read ahead one more node than
- * otherwise required (to check for trailing marker) rather than
- * unmasking mark bits or whatever on each read.
- *
- * This approach maintains the essential property needed in the HM
- * algorithm of changing the next-pointer of a deleted node so
- * that any other CAS of it will fail, but implements the idea by
- * changing the pointer to point to a different node, not by
- * marking it. While it would be possible to further squeeze
- * space by defining marker nodes not to have key/value fields, it
- * isn't worth the extra type-testing overhead. The deletion
- * markers are rarely encountered during traversal and are
- * normally quickly garbage collected. (Note that this technique
- * would not work well in systems without garbage collection.)
- *
- * In addition to using deletion markers, the lists also use
- * nullness of value fields to indicate deletion, in a style
- * similar to typical lazy-deletion schemes. If a node's value is
- * null, then it is considered logically deleted and ignored even
- * though it is still reachable. This maintains proper control of
- * concurrent replace vs delete operations -- an attempted replace
- * must fail if a delete beat it by nulling field, and a delete
- * must return the last non-null value held in the field. (Note:
- * Null, rather than some special marker, is used for value fields
- * here because it just so happens to mesh with the Map API
- * requirement that method get returns null if there is no
- * mapping, which allows nodes to remain concurrently readable
- * even when deleted. Using any other marker value here would be
- * messy at best.)
- *
- * Here's the sequence of events for a deletion of node n with
- * predecessor b and successor f, initially:
- *
- * +------+ +------+ +------+
- * ... | b |------>| n |----->| f | ...
- * +------+ +------+ +------+
- *
- * 1. CAS n's value field from non-null to null.
- * From this point on, no public operations encountering
- * the node consider this mapping to exist. However, other
- * ongoing insertions and deletions might still modify
- * n's next pointer.
- *
- * 2. CAS n's next pointer to point to a new marker node.
- * From this point on, no other nodes can be appended to n.
- * which avoids deletion errors in CAS-based linked lists.
- *
- * +------+ +------+ +------+ +------+
- * ... | b |------>| n |----->|marker|------>| f | ...
- * +------+ +------+ +------+ +------+
- *
- * 3. CAS b's next pointer over both n and its marker.
- * From this point on, no new traversals will encounter n,
- * and it can eventually be GCed.
- * +------+ +------+
- * ... | b |----------------------------------->| f | ...
- * +------+ +------+
- *
- * A failure at step 1 leads to simple retry due to a lost race
- * with another operation. Steps 2-3 can fail because some other
- * thread noticed during a traversal a node with null value and
- * helped out by marking and/or unlinking. This helping-out
- * ensures that no thread can become stuck waiting for progress of
- * the deleting thread. The use of marker nodes slightly
- * complicates helping-out code because traversals must track
- * consistent reads of up to four nodes (b, n, marker, f), not
- * just (b, n, f), although the next field of a marker is
- * immutable, and once a next field is CAS'ed to point to a
- * marker, it never again changes, so this requires less care.
- *
- * Skip lists add indexing to this scheme, so that the base-level
- * traversals start close to the locations being found, inserted
- * or deleted -- usually base level traversals only traverse a few
- * nodes. This doesn't change the basic algorithm except for the
- * need to make sure base traversals start at predecessors (here,
- * b) that are not (structurally) deleted, otherwise retrying
- * after processing the deletion.
- *
- * Index levels are maintained as lists with volatile next fields,
- * using CAS to link and unlink. Races are allowed in index-list
- * operations that can (rarely) fail to link in a new index node
- * or delete one. (We can't do this of course for data nodes.)
- * However, even when this happens, the index lists remain sorted,
- * so correctly serve as indices. This can impact performance,
- * but since skip lists are probabilistic anyway, the net result
- * is that under contention, the effective "p" value may be lower
- * than its nominal value. And race windows are kept small enough
- * that in practice these failures are rare, even under a lot of
- * contention.
- *
- * The fact that retries (for both base and index lists) are
- * relatively cheap due to indexing allows some minor
- * simplifications of retry logic. Traversal restarts are
- * performed after most "helping-out" CASes. This isn't always
- * strictly necessary, but the implicit backoffs tend to help
- * reduce other downstream failed CAS's enough to outweigh restart
- * cost. This worsens the worst case, but seems to improve even
- * highly contended cases.
- *
- * Unlike most skip-list implementations, index insertion and
- * deletion here require a separate traversal pass occuring after
- * the base-level action, to add or remove index nodes. This adds
- * to single-threaded overhead, but improves contended
- * multithreaded performance by narrowing interference windows,
- * and allows deletion to ensure that all index nodes will be made
- * unreachable upon return from a public remove operation, thus
- * avoiding unwanted garbage retention. This is more important
- * here than in some other data structures because we cannot null
- * out node fields referencing user keys since they might still be
- * read by other ongoing traversals.
- *
- * Indexing uses skip list parameters that maintain good search
- * performance while using sparser-than-usual indices: The
- * hardwired parameters k=1, p=0.5 (see method randomLevel) mean
- * that about one-quarter of the nodes have indices. Of those that
- * do, half have one level, a quarter have two, and so on (see
- * Pugh's Skip List Cookbook, sec 3.4). The expected total space
- * requirement for a map is slightly less than for the current
- * implementation of java.util.TreeMap.
- *
- * Changing the level of the index (i.e, the height of the
- * tree-like structure) also uses CAS. The head index has initial
- * level/height of one. Creation of an index with height greater
- * than the current level adds a level to the head index by
- * CAS'ing on a new top-most head. To maintain good performance
- * after a lot of removals, deletion methods heuristically try to
- * reduce the height if the topmost levels appear to be empty.
- * This may encounter races in which it possible (but rare) to
- * reduce and "lose" a level just as it is about to contain an
- * index (that will then never be encountered). This does no
- * structural harm, and in practice appears to be a better option
- * than allowing unrestrained growth of levels.
- *
- * The code for all this is more verbose than you'd like. Most
- * operations entail locating an element (or position to insert an
- * element). The code to do this can't be nicely factored out
- * because subsequent uses require a snapshot of predecessor
- * and/or successor and/or value fields which can't be returned
- * all at once, at least not without creating yet another object
- * to hold them -- creating such little objects is an especially
- * bad idea for basic internal search operations because it adds
- * to GC overhead. (This is one of the few times I've wished Java
- * had macros.) Instead, some traversal code is interleaved within
- * insertion and removal operations. The control logic to handle
- * all the retry conditions is sometimes twisty. Most search is
- * broken into 2 parts. findPredecessor() searches index nodes
- * only, returning a base-level predecessor of the key. findNode()
- * finishes out the base-level search. Even with this factoring,
- * there is a fair amount of near-duplication of code to handle
- * variants.
- *
- * For explanation of algorithms sharing at least a couple of
- * features with this one, see Mikhail Fomitchev's thesis
- * (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis
- * (http://www.cl.cam.ac.uk/users/kaf24/), and Hakan Sundell's
- * thesis (http://www.cs.chalmers.se/~phs/).
- *
- * Given the use of tree-like index nodes, you might wonder why
- * this doesn't use some kind of search tree instead, which would
- * support somewhat faster search operations. The reason is that
- * there are no known efficient lock-free insertion and deletion
- * algorithms for search trees. The immutability of the "down"
- * links of index nodes (as opposed to mutable "left" fields in
- * true trees) makes this tractable using only CAS operations.
- *
- * Notation guide for local variables
- * Node: b, n, f for predecessor, node, successor
- * Index: q, r, d for index node, right, down.
- * t for another index node
- * Head: h
- * Levels: j
- * Keys: k, key
- * Values: v, value
- * Comparisons: c
- */
-
- private static final long serialVersionUID = -8627078645895051609L;
-
- /**
- * Generates the initial random seed for the cheaper per-instance
- * random number generators used in randomLevel.
- */
- private static final Random seedGenerator = new Random();
-
- /**
- * Special value used to identify base-level header
- */
- private static final Object BASE_HEADER = new Object();
-
- /**
- * The topmost head index of the skiplist.
- */
- private transient volatile HeadIndex<K,V> head;
-
- /**
- * The comparator used to maintain order in this map, or null
- * if using natural ordering.
- * @serial
- */
- private final Comparator<? super K> comparator;
-
- /**
- * Seed for simple random number generator. Not volatile since it
- * doesn't matter too much if different threads don't see updates.
- */
- private transient int randomSeed;
-
- /** Lazily initialized key set */
- private transient KeySet keySet;
- /** Lazily initialized entry set */
- private transient EntrySet entrySet;
- /** Lazily initialized values collection */
- private transient Values values;
- /** Lazily initialized descending key set */
- private transient ConcurrentNavigableMap<K,V> descendingMap;
-
- /**
- * Initializes or resets state. Needed by constructors, clone,
- * clear, readObject. and ConcurrentSkipListSet.clone.
- * (Note that comparator must be separately initialized.)
- */
- final void initialize() {
- keySet = null;
- entrySet = null;
- values = null;
- descendingMap = null;
- randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero
- head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
- null, null, 1);
- }
-
- /** Updater for casHead */
- private static final
- AtomicReferenceFieldUpdater<ConcurrentSkipListMap, HeadIndex>
- headUpdater = AtomicReferenceFieldUpdater.newUpdater
- (ConcurrentSkipListMap.class, HeadIndex.class, "head");
-
- /**
- * compareAndSet head node
- */
- private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
- return headUpdater.compareAndSet(this, cmp, val);
- }
-
- /* ---------------- Nodes -------------- */
-
- /**
- * Nodes hold keys and values, and are singly linked in sorted
- * order, possibly with some intervening marker nodes. The list is
- * headed by a dummy node accessible as head.node. The value field
- * is declared only as Object because it takes special non-V
- * values for marker and header nodes.
- */
- static final class Node<K,V> {
- final K key;
- volatile Object value;
- volatile Node<K,V> next;
-
- /**
- * Creates a new regular node.
- */
- Node(K key, Object value, Node<K,V> next) {
- this.key = key;
- this.value = value;
- this.next = next;
- }
-
- /**
- * Creates a new marker node. A marker is distinguished by
- * having its value field point to itself. Marker nodes also
- * have null keys, a fact that is exploited in a few places,
- * but this doesn't distinguish markers from the base-level
- * header node (head.node), which also has a null key.
- */
- Node(Node<K,V> next) {
- this.key = null;
- this.value = this;
- this.next = next;
- }
-
- /** Updater for casNext */
- static final AtomicReferenceFieldUpdater<Node, Node>
- nextUpdater = AtomicReferenceFieldUpdater.newUpdater
- (Node.class, Node.class, "next");
-
- /** Updater for casValue */
- static final AtomicReferenceFieldUpdater<Node, Object>
- valueUpdater = AtomicReferenceFieldUpdater.newUpdater
- (Node.class, Object.class, "value");
-
- /**
- * compareAndSet value field
- */
- boolean casValue(Object cmp, Object val) {
- return valueUpdater.compareAndSet(this, cmp, val);
- }
-
- /**
- * compareAndSet next field
- */
- boolean casNext(Node<K,V> cmp, Node<K,V> val) {
- return nextUpdater.compareAndSet(this, cmp, val);
- }
-
- /**
- * Returns true if this node is a marker. This method isn't
- * actually called in any current code checking for markers
- * because callers will have already read value field and need
- * to use that read (not another done here) and so directly
- * test if value points to node.
- * @param n a possibly null reference to a node
- * @return true if this node is a marker node
- */
- boolean isMarker() {
- return value == this;
- }
-
- /**
- * Returns true if this node is the header of base-level list.
- * @return true if this node is header node
- */
- boolean isBaseHeader() {
- return value == BASE_HEADER;
- }
-
- /**
- * Tries to append a deletion marker to this node.
- * @param f the assumed current successor of this node
- * @return true if successful
- */
- boolean appendMarker(Node<K,V> f) {
- return casNext(f, new Node<K,V>(f));
- }
-
- /**
- * Helps out a deletion by appending marker or unlinking from
- * predecessor. This is called during traversals when value
- * field seen to be null.
- * @param b predecessor
- * @param f successor
- */
- void helpDelete(Node<K,V> b, Node<K,V> f) {
- /*
- * Rechecking links and then doing only one of the
- * help-out stages per call tends to minimize CAS
- * interference among helping threads.
- */
- if (f == next && this == b.next) {
- if (f == null || f.value != f) // not already marked
- appendMarker(f);
- else
- b.casNext(this, f.next);
- }
- }
-
- /**
- * Returns value if this node contains a valid key-value pair,
- * else null.
- * @return this node's value if it isn't a marker or header or
- * is deleted, else null.
- */
- V getValidValue() {
- Object v = value;
- if (v == this || v == BASE_HEADER)
- return null;
- return (V)v;
- }
-
- /**
- * Creates and returns a new SimpleImmutableEntry holding current
- * mapping if this node holds a valid value, else null.
- * @return new entry or null
- */
- AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
- V v = getValidValue();
- if (v == null)
- return null;
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
- }
- }
-
- /* ---------------- Indexing -------------- */
-
- /**
- * Index nodes represent the levels of the skip list. Note that
- * even though both Nodes and Indexes have forward-pointing
- * fields, they have different types and are handled in different
- * ways, that can't nicely be captured by placing field in a
- * shared abstract class.
- */
- static class Index<K,V> {
- final Node<K,V> node;
- final Index<K,V> down;
- volatile Index<K,V> right;
-
- /**
- * Creates index node with given values.
- */
- Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
- this.node = node;
- this.down = down;
- this.right = right;
- }
-
- /** Updater for casRight */
- static final AtomicReferenceFieldUpdater<Index, Index>
- rightUpdater = AtomicReferenceFieldUpdater.newUpdater
- (Index.class, Index.class, "right");
-
- /**
- * compareAndSet right field
- */
- final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
- return rightUpdater.compareAndSet(this, cmp, val);
- }
-
- /**
- * Returns true if the node this indexes has been deleted.
- * @return true if indexed node is known to be deleted
- */
- final boolean indexesDeletedNode() {
- return node.value == null;
- }
-
- /**
- * Tries to CAS newSucc as successor. To minimize races with
- * unlink that may lose this index node, if the node being
- * indexed is known to be deleted, it doesn't try to link in.
- * @param succ the expected current successor
- * @param newSucc the new successor
- * @return true if successful
- */
- final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
- Node<K,V> n = node;
- newSucc.right = succ;
- return n.value != null && casRight(succ, newSucc);
- }
-
- /**
- * Tries to CAS right field to skip over apparent successor
- * succ. Fails (forcing a retraversal by caller) if this node
- * is known to be deleted.
- * @param succ the expected current successor
- * @return true if successful
- */
- final boolean unlink(Index<K,V> succ) {
- return !indexesDeletedNode() && casRight(succ, succ.right);
- }
- }
-
- /* ---------------- Head nodes -------------- */
-
- /**
- * Nodes heading each level keep track of their level.
- */
- static final class HeadIndex<K,V> extends Index<K,V> {
- final int level;
- HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
- super(node, down, right);
- this.level = level;
- }
- }
-
- /* ---------------- Comparison utilities -------------- */
-
- /**
- * Represents a key with a comparator as a Comparable.
- *
- * Because most sorted collections seem to use natural ordering on
- * Comparables (Strings, Integers, etc), most internal methods are
- * geared to use them. This is generally faster than checking
- * per-comparison whether to use comparator or comparable because
- * it doesn't require a (Comparable) cast for each comparison.
- * (Optimizers can only sometimes remove such redundant checks
- * themselves.) When Comparators are used,
- * ComparableUsingComparators are created so that they act in the
- * same way as natural orderings. This penalizes use of
- * Comparators vs Comparables, which seems like the right
- * tradeoff.
- */
- static final class ComparableUsingComparator<K> implements Comparable<K> {
- final K actualKey;
- final Comparator<? super K> cmp;
- ComparableUsingComparator(K key, Comparator<? super K> cmp) {
- this.actualKey = key;
- this.cmp = cmp;
- }
- public int compareTo(K k2) {
- return cmp.compare(actualKey, k2);
- }
- }
-
- /**
- * If using comparator, return a ComparableUsingComparator, else
- * cast key as Comparable, which may cause ClassCastException,
- * which is propagated back to caller.
- */
- private Comparable<? super K> comparable(Object key) throws ClassCastException {
- if (key == null)
- throw new NullPointerException();
- if (comparator != null)
- return new ComparableUsingComparator<K>((K)key, comparator);
- else
- return (Comparable<? super K>)key;
- }
-
- /**
- * Compares using comparator or natural ordering. Used when the
- * ComparableUsingComparator approach doesn't apply.
- */
- int compare(K k1, K k2) throws ClassCastException {
- Comparator<? super K> cmp = comparator;
- if (cmp != null)
- return cmp.compare(k1, k2);
- else
- return ((Comparable<? super K>)k1).compareTo(k2);
- }
-
- /**
- * Returns true if given key greater than or equal to least and
- * strictly less than fence, bypassing either test if least or
- * fence are null. Needed mainly in submap operations.
- */
- boolean inHalfOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) < 0));
- }
-
- /**
- * Returns true if given key greater than or equal to least and less
- * or equal to fence. Needed mainly in submap operations.
- */
- boolean inOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) <= 0));
- }
-
- /* ---------------- Traversal -------------- */
-
- /**
- * Returns a base-level node with key strictly less than given key,
- * or the base-level header if there is no such node. Also
- * unlinks indexes to deleted nodes found along the way. Callers
- * rely on this side-effect of clearing indices to deleted nodes.
- * @param key the key
- * @return a predecessor of key
- */
- private Node<K,V> findPredecessor(Comparable<? super K> key) {
- if (key == null)
- throw new NullPointerException(); // don't postpone errors
- for (;;) {
- Index<K,V> q = head;
- Index<K,V> r = q.right;
- for (;;) {
- if (r != null) {
- Node<K,V> n = r.node;
- K k = n.key;
- if (n.value == null) {
- if (!q.unlink(r))
- break; // restart
- r = q.right; // reread r
- continue;
- }
- if (key.compareTo(k) > 0) {
- q = r;
- r = r.right;
- continue;
- }
- }
- Index<K,V> d = q.down;
- if (d != null) {
- q = d;
- r = d.right;
- } else
- return q.node;
- }
- }
- }
-
- /**
- * Returns node holding key or null if no such, clearing out any
- * deleted nodes seen along the way. Repeatedly traverses at
- * base-level looking for key starting at predecessor returned
- * from findPredecessor, processing base-level deletions as
- * encountered. Some callers rely on this side-effect of clearing
- * deleted nodes.
- *
- * Restarts occur, at traversal step centered on node n, if:
- *
- * (1) After reading n's next field, n is no longer assumed
- * predecessor b's current successor, which means that
- * we don't have a consistent 3-node snapshot and so cannot
- * unlink any subsequent deleted nodes encountered.
- *
- * (2) n's value field is null, indicating n is deleted, in
- * which case we help out an ongoing structural deletion
- * before retrying. Even though there are cases where such
- * unlinking doesn't require restart, they aren't sorted out
- * here because doing so would not usually outweigh cost of
- * restarting.
- *
- * (3) n is a marker or n's predecessor's value field is null,
- * indicating (among other possibilities) that
- * findPredecessor returned a deleted node. We can't unlink
- * the node because we don't know its predecessor, so rely
- * on another call to findPredecessor to notice and return
- * some earlier predecessor, which it will do. This check is
- * only strictly needed at beginning of loop, (and the
- * b.value check isn't strictly needed at all) but is done
- * each iteration to help avoid contention with other
- * threads by callers that will fail to be able to change
- * links, and so will retry anyway.
- *
- * The traversal loops in doPut, doRemove, and findNear all
- * include the same three kinds of checks. And specialized
- * versions appear in findFirst, and findLast and their
- * variants. They can't easily share code because each uses the
- * reads of fields held in locals occurring in the orders they
- * were performed.
- *
- * @param key the key
- * @return node holding key, or null if no such
- */
- private Node<K,V> findNode(Comparable<? super K> key) {
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
- if (n == null)
- return null;
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- int c = key.compareTo(n.key);
- if (c == 0)
- return n;
- if (c < 0)
- return null;
- b = n;
- n = f;
- }
- }
- }
-
- /**
- * Specialized variant of findNode to perform Map.get. Does a weak
- * traversal, not bothering to fix any deleted index nodes,
- * returning early if it happens to see key in index, and passing
- * over any deleted base nodes, falling back to getUsingFindNode
- * only if it would otherwise return value from an ongoing
- * deletion. Also uses "bound" to eliminate need for some
- * comparisons (see Pugh Cookbook). Also folds uses of null checks
- * and node-skipping because markers have null keys.
- * @param okey the key
- * @return the value, or null if absent
- */
- private V doGet(Object okey) {
- Comparable<? super K> key = comparable(okey);
- Node<K,V> bound = null;
- Index<K,V> q = head;
- Index<K,V> r = q.right;
- Node<K,V> n;
- K k;
- int c;
- for (;;) {
- Index<K,V> d;
- // Traverse rights
- if (r != null && (n = r.node) != bound && (k = n.key) != null) {
- if ((c = key.compareTo(k)) > 0) {
- q = r;
- r = r.right;
- continue;
- } else if (c == 0) {
- Object v = n.value;
- return (v != null)? (V)v : getUsingFindNode(key);
- } else
- bound = n;
- }
-
- // Traverse down
- if ((d = q.down) != null) {
- q = d;
- r = d.right;
- } else
- break;
- }
-
- // Traverse nexts
- for (n = q.node.next; n != null; n = n.next) {
- if ((k = n.key) != null) {
- if ((c = key.compareTo(k)) == 0) {
- Object v = n.value;
- return (v != null)? (V)v : getUsingFindNode(key);
- } else if (c < 0)
- break;
- }
- }
- return null;
- }
-
- /**
- * Performs map.get via findNode. Used as a backup if doGet
- * encounters an in-progress deletion.
- * @param key the key
- * @return the value, or null if absent
- */
- private V getUsingFindNode(Comparable<? super K> key) {
- /*
- * Loop needed here and elsewhere in case value field goes
- * null just as it is about to be returned, in which case we
- * lost a race with a deletion, so must retry.
- */
- for (;;) {
- Node<K,V> n = findNode(key);
- if (n == null)
- return null;
- Object v = n.value;
- if (v != null)
- return (V)v;
- }
- }
-
- /* ---------------- Insertion -------------- */
-
- /**
- * Main insertion method. Adds element if not present, or
- * replaces value if present and onlyIfAbsent is false.
- * @param kkey the key
- * @param value the value that must be associated with key
- * @param onlyIfAbsent if should not insert if already present
- * @return the old value, or null if newly inserted
- */
- private V doPut(K kkey, V value, boolean onlyIfAbsent) {
- Comparable<? super K> key = comparable(kkey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
- if (n != null) {
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- int c = key.compareTo(n.key);
- if (c > 0) {
- b = n;
- n = f;
- continue;
- }
- if (c == 0) {
- if (onlyIfAbsent || n.casValue(v, value))
- return (V)v;
- else
- break; // restart if lost race to replace value
- }
- // else c < 0; fall through
- }
-
- Node<K,V> z = new Node<K,V>(kkey, value, n);
- if (!b.casNext(n, z))
- break; // restart if lost race to append to b
- int level = randomLevel();
- if (level > 0)
- insertIndex(z, level);
- return null;
- }
- }
- }
-
- /**
- * Returns a random level for inserting a new node.
- * Hardwired to k=1, p=0.5, max 31 (see above and
- * Pugh's "Skip List Cookbook", sec 3.4).
- *
- * This uses the simplest of the generators described in George
- * Marsaglia's "Xorshift RNGs" paper. This is not a high-quality
- * generator but is acceptable here.
- */
- private int randomLevel() {
- int x = randomSeed;
- x ^= x << 13;
- x ^= x >>> 17;
- randomSeed = x ^= x << 5;
- if ((x & 0x8001) != 0) // test highest and lowest bits
- return 0;
- int level = 1;
- while (((x >>>= 1) & 1) != 0) ++level;
- return level;
- }
-
- /**
- * Creates and adds index nodes for the given node.
- * @param z the node
- * @param level the level of the index
- */
- private void insertIndex(Node<K,V> z, int level) {
- HeadIndex<K,V> h = head;
- int max = h.level;
-
- if (level <= max) {
- Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idx = new Index<K,V>(z, idx, null);
- addIndex(idx, h, level);
-
- } else { // Add a new level
- /*
- * To reduce interference by other threads checking for
- * empty levels in tryReduceLevel, new levels are added
- * with initialized right pointers. Which in turn requires
- * keeping levels in an array to access them while
- * creating new head index nodes from the opposite
- * direction.
- */
- level = max + 1;
- Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1];
- Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idxs[i] = idx = new Index<K,V>(z, idx, null);
-
- HeadIndex<K,V> oldh;
- int k;
- for (;;) {
- oldh = head;
- int oldLevel = oldh.level;
- if (level <= oldLevel) { // lost race to add level
- k = level;
- break;
- }
- HeadIndex<K,V> newh = oldh;
- Node<K,V> oldbase = oldh.node;
- for (int j = oldLevel+1; j <= level; ++j)
- newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
- if (casHead(oldh, newh)) {
- k = oldLevel;
- break;
- }
- }
- addIndex(idxs[k], oldh, k);
- }
- }
-
- /**
- * Adds given index nodes from given level down to 1.
- * @param idx the topmost index node being inserted
- * @param h the value of head to use to insert. This must be
- * snapshotted by callers to provide correct insertion level
- * @param indexLevel the level of the index
- */
- private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) {
- // Track next level to insert in case of retries
- int insertionLevel = indexLevel;
- Comparable<? super K> key = comparable(idx.node.key);
- if (key == null) throw new NullPointerException();
-
- // Similar to findPredecessor, but adding index nodes along
- // path to key.
- for (;;) {
- int j = h.level;
- Index<K,V> q = h;
- Index<K,V> r = q.right;
- Index<K,V> t = idx;
- for (;;) {
- if (r != null) {
- Node<K,V> n = r.node;
- // compare before deletion check avoids needing recheck
- int c = key.compareTo(n.key);
- if (n.value == null) {
- if (!q.unlink(r))
- break;
- r = q.right;
- continue;
- }
- if (c > 0) {
- q = r;
- r = r.right;
- continue;
- }
- }
-
- if (j == insertionLevel) {
- // Don't insert index if node already deleted
- if (t.indexesDeletedNode()) {
- findNode(key); // cleans up
- return;
- }
- if (!q.link(r, t))
- break; // restart
- if (--insertionLevel == 0) {
- // need final deletion check before return
- if (t.indexesDeletedNode())
- findNode(key);
- return;
- }
- }
-
- if (--j >= insertionLevel && j < indexLevel)
- t = t.down;
- q = q.down;
- r = q.right;
- }
- }
- }
-
- /* ---------------- Deletion -------------- */
-
- /**
- * Main deletion method. Locates node, nulls value, appends a
- * deletion marker, unlinks predecessor, removes associated index
- * nodes, and possibly reduces head index level.
- *
- * Index nodes are cleared out simply by calling findPredecessor.
- * which unlinks indexes to deleted nodes found along path to key,
- * which will include the indexes to this node. This is done
- * unconditionally. We can't check beforehand whether there are
- * index nodes because it might be the case that some or all
- * indexes hadn't been inserted yet for this node during initial
- * search for it, and we'd like to ensure lack of garbage
- * retention, so must call to be sure.
- *
- * @param okey the key
- * @param value if non-null, the value that must be
- * associated with key
- * @return the node, or null if not found
- */
- final V doRemove(Object okey, Object value) {
- Comparable<? super K> key = comparable(okey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
- if (n == null)
- return null;
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- int c = key.compareTo(n.key);
- if (c < 0)
- return null;
- if (c > 0) {
- b = n;
- n = f;
- continue;
- }
- if (value != null && !value.equals(v))
- return null;
- if (!n.casValue(v, null))
- break;
- if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(key); // Retry via findNode
- else {
- findPredecessor(key); // Clean index
- if (head.right == null)
- tryReduceLevel();
- }
- return (V)v;
- }
- }
- }
-
- /**
- * Possibly reduce head level if it has no nodes. This method can
- * (rarely) make mistakes, in which case levels can disappear even
- * though they are about to contain index nodes. This impacts
- * performance, not correctness. To minimize mistakes as well as
- * to reduce hysteresis, the level is reduced by one only if the
- * topmost three levels look empty. Also, if the removed level
- * looks non-empty after CAS, we try to change it back quick
- * before anyone notices our mistake! (This trick works pretty
- * well because this method will practically never make mistakes
- * unless current thread stalls immediately before first CAS, in
- * which case it is very unlikely to stall again immediately
- * afterwards, so will recover.)
- *
- * We put up with all this rather than just let levels grow
- * because otherwise, even a small map that has undergone a large
- * number of insertions and removals will have a lot of levels,
- * slowing down access more than would an occasional unwanted
- * reduction.
- */
- private void tryReduceLevel() {
- HeadIndex<K,V> h = head;
- HeadIndex<K,V> d;
- HeadIndex<K,V> e;
- if (h.level > 3 &&
- (d = (HeadIndex<K,V>)h.down) != null &&
- (e = (HeadIndex<K,V>)d.down) != null &&
- e.right == null &&
- d.right == null &&
- h.right == null &&
- casHead(h, d) && // try to set
- h.right != null) // recheck
- casHead(d, h); // try to backout
- }
-
- /* ---------------- Finding and removing first element -------------- */
-
- /**
- * Specialized variant of findNode to get first valid node.
- * @return first node or null if empty
- */
- Node<K,V> findFirst() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
- return null;
- if (n.value != null)
- return n;
- n.helpDelete(b, n.next);
- }
- }
-
- /**
- * Removes first entry; returns its snapshot.
- * @return null if empty, else snapshot of first entry
- */
- Map.Entry<K,V> doRemoveFirstEntry() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
- return null;
- Node<K,V> f = n.next;
- if (n != b.next)
- continue;
- Object v = n.value;
- if (v == null) {
- n.helpDelete(b, f);
- continue;
- }
- if (!n.casValue(v, null))
- continue;
- if (!n.appendMarker(f) || !b.casNext(n, f))
- findFirst(); // retry
- clearIndexToFirst();
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, (V)v);
- }
- }
-
- /**
- * Clears out index nodes associated with deleted first entry.
- */
- private void clearIndexToFirst() {
- for (;;) {
- Index<K,V> q = head;
- for (;;) {
- Index<K,V> r = q.right;
- if (r != null && r.indexesDeletedNode() && !q.unlink(r))
- break;
- if ((q = q.down) == null) {
- if (head.right == null)
- tryReduceLevel();
- return;
- }
- }
- }
- }
-
-
- /* ---------------- Finding and removing last element -------------- */
-
- /**
- * Specialized version of find to get last valid node.
- * @return last node or null if empty
- */
- Node<K,V> findLast() {
- /*
- * findPredecessor can't be used to traverse index level
- * because this doesn't use comparisons. So traversals of
- * both levels are folded together.
- */
- Index<K,V> q = head;
- for (;;) {
- Index<K,V> d, r;
- if ((r = q.right) != null) {
- if (r.indexesDeletedNode()) {
- q.unlink(r);
- q = head; // restart
- }
- else
- q = r;
- } else if ((d = q.down) != null) {
- q = d;
- } else {
- Node<K,V> b = q.node;
- Node<K,V> n = b.next;
- for (;;) {
- if (n == null)
- return (b.isBaseHeader())? null : b;
- Node<K,V> f = n.next; // inconsistent read
- if (n != b.next)
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- b = n;
- n = f;
- }
- q = head; // restart
- }
- }
- }
-
- /**
- * Specialized variant of findPredecessor to get predecessor of last
- * valid node. Needed when removing the last entry. It is possible
- * that all successors of returned node will have been deleted upon
- * return, in which case this method can be retried.
- * @return likely predecessor of last node
- */
- private Node<K,V> findPredecessorOfLast() {
- for (;;) {
- Index<K,V> q = head;
- for (;;) {
- Index<K,V> d, r;
- if ((r = q.right) != null) {
- if (r.indexesDeletedNode()) {
- q.unlink(r);
- break; // must restart
- }
- // proceed as far across as possible without overshooting
- if (r.node.next != null) {
- q = r;
- continue;
- }
- }
- if ((d = q.down) != null)
- q = d;
- else
- return q.node;
- }
- }
- }
-
- /**
- * Removes last entry; returns its snapshot.
- * Specialized variant of doRemove.
- * @return null if empty, else snapshot of last entry
- */
- Map.Entry<K,V> doRemoveLastEntry() {
- for (;;) {
- Node<K,V> b = findPredecessorOfLast();
- Node<K,V> n = b.next;
- if (n == null) {
- if (b.isBaseHeader()) // empty
- return null;
- else
- continue; // all b's successors are deleted; retry
- }
- for (;;) {
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- if (f != null) {
- b = n;
- n = f;
- continue;
- }
- if (!n.casValue(v, null))
- break;
- K key = n.key;
- Comparable<? super K> ck = comparable(key);
- if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(ck); // Retry via findNode
- else {
- findPredecessor(ck); // Clean index
- if (head.right == null)
- tryReduceLevel();
- }
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, (V)v);
- }
- }
- }
-
- /* ---------------- Relational operations -------------- */
-
- // Control values OR'ed as arguments to findNear
-
- private static final int EQ = 1;
- private static final int LT = 2;
- private static final int GT = 0; // Actually checked as !LT
-
- /**
- * Utility for ceiling, floor, lower, higher methods.
- * @param kkey the key
- * @param rel the relation -- OR'ed combination of EQ, LT, GT
- * @return nearest node fitting relation, or null if no such
- */
- Node<K,V> findNear(K kkey, int rel) {
- Comparable<? super K> key = comparable(kkey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
- if (n == null)
- return ((rel & LT) == 0 || b.isBaseHeader())? null : b;
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- int c = key.compareTo(n.key);
- if ((c == 0 && (rel & EQ) != 0) ||
- (c < 0 && (rel & LT) == 0))
- return n;
- if ( c <= 0 && (rel & LT) != 0)
- return (b.isBaseHeader())? null : b;
- b = n;
- n = f;
- }
- }
- }
-
- /**
- * Returns SimpleImmutableEntry for results of findNear.
- * @param key the key
- * @param rel the relation -- OR'ed combination of EQ, LT, GT
- * @return Entry fitting relation, or null if no such
- */
- AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
- for (;;) {
- Node<K,V> n = findNear(key, rel);
- if (n == null)
- return null;
- AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
- if (e != null)
- return e;
- }
- }
-
-
- /* ---------------- Constructors -------------- */
-
- /**
- * Constructs a new, empty map, sorted according to the
- * {@linkplain Comparable natural ordering} of the keys.
- */
- public ConcurrentSkipListMap() {
- this.comparator = null;
- initialize();
- }
-
- /**
- * Constructs a new, empty map, sorted according to the specified
- * comparator.
- *
- * @param comparator the comparator that will be used to order this map.
- * If <tt>null</tt>, the {@linkplain Comparable natural
- * ordering} of the keys will be used.
- */
- public ConcurrentSkipListMap(Comparator<? super K> comparator) {
- this.comparator = comparator;
- initialize();
- }
-
- /**
- * Constructs a new map containing the same mappings as the given map,
- * sorted according to the {@linkplain Comparable natural ordering} of
- * the keys.
- *
- * @param m the map whose mappings are to be placed in this map
- * @throws ClassCastException if the keys in <tt>m</tt> are not
- * {@link Comparable}, or are not mutually comparable
- * @throws NullPointerException if the specified map or any of its keys
- * or values are null
- */
- public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
- this.comparator = null;
- initialize();
- putAll(m);
- }
-
- /**
- * Constructs a new map containing the same mappings and using the
- * same ordering as the specified sorted map.
- *
- * @param m the sorted map whose mappings are to be placed in this
- * map, and whose comparator is to be used to sort this map
- * @throws NullPointerException if the specified sorted map or any of
- * its keys or values are null
- */
- public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
- this.comparator = m.comparator();
- initialize();
- buildFromSorted(m);
- }
-
- /**
- * Returns a shallow copy of this <tt>ConcurrentSkipListMap</tt>
- * instance. (The keys and values themselves are not cloned.)
- *
- * @return a shallow copy of this map
- */
- public ConcurrentSkipListMap<K,V> clone() {
- ConcurrentSkipListMap<K,V> clone = null;
- try {
- clone = (ConcurrentSkipListMap<K,V>) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new InternalError();
- }
-
- clone.initialize();
- clone.buildFromSorted(this);
- return clone;
- }
-
- /**
- * Streamlined bulk insertion to initialize from elements of
- * given sorted map. Call only from constructor or clone
- * method.
- */
- private void buildFromSorted(SortedMap<K, ? extends V> map) {
- if (map == null)
- throw new NullPointerException();
-
- HeadIndex<K,V> h = head;
- Node<K,V> basepred = h.node;
-
- // Track the current rightmost node at each level. Uses an
- // ArrayList to avoid committing to initial or maximum level.
- ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
-
- // initialize
- for (int i = 0; i <= h.level; ++i)
- preds.add(null);
- Index<K,V> q = h;
- for (int i = h.level; i > 0; --i) {
- preds.set(i, q);
- q = q.down;
- }
-
- Iterator<? extends Map.Entry<? extends K, ? extends V>> it =
- map.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<? extends K, ? extends V> e = it.next();
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
- K k = e.getKey();
- V v = e.getValue();
- if (k == null || v == null)
- throw new NullPointerException();
- Node<K,V> z = new Node<K,V>(k, v, null);
- basepred.next = z;
- basepred = z;
- if (j > 0) {
- Index<K,V> idx = null;
- for (int i = 1; i <= j; ++i) {
- idx = new Index<K,V>(z, idx, null);
- if (i > h.level)
- h = new HeadIndex<K,V>(h.node, h, idx, i);
-
- if (i < preds.size()) {
- preds.get(i).right = idx;
- preds.set(i, idx);
- } else
- preds.add(idx);
- }
- }
- }
- head = h;
- }
-
- /* ---------------- Serialization -------------- */
-
- /**
- * Save the state of this map to a stream.
- *
- * @serialData The key (Object) and value (Object) for each
- * key-value mapping represented by the map, followed by
- * <tt>null</tt>. The key-value mappings are emitted in key-order
- * (as determined by the Comparator, or by the keys' natural
- * ordering if no Comparator).
- */
- private void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
- // Write out the Comparator and any hidden stuff
- s.defaultWriteObject();
-
- // Write out keys and values (alternating)
- for (Node<K,V> n = findFirst(); n != null; n = n.next) {
- V v = n.getValidValue();
- if (v != null) {
- s.writeObject(n.key);
- s.writeObject(v);
- }
- }
- s.writeObject(null);
- }
-
- /**
- * Reconstitute the map from a stream.
- */
- private void readObject(final java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- // Read in the Comparator and any hidden stuff
- s.defaultReadObject();
- // Reset transients
- initialize();
-
- /*
- * This is nearly identical to buildFromSorted, but is
- * distinct because readObject calls can't be nicely adapted
- * as the kind of iterator needed by buildFromSorted. (They
- * can be, but doing so requires type cheats and/or creation
- * of adaptor classes.) It is simpler to just adapt the code.
- */
-
- HeadIndex<K,V> h = head;
- Node<K,V> basepred = h.node;
- ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
- for (int i = 0; i <= h.level; ++i)
- preds.add(null);
- Index<K,V> q = h;
- for (int i = h.level; i > 0; --i) {
- preds.set(i, q);
- q = q.down;
- }
-
- for (;;) {
- Object k = s.readObject();
- if (k == null)
- break;
- Object v = s.readObject();
- if (v == null)
- throw new NullPointerException();
- K key = (K) k;
- V val = (V) v;
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
- Node<K,V> z = new Node<K,V>(key, val, null);
- basepred.next = z;
- basepred = z;
- if (j > 0) {
- Index<K,V> idx = null;
- for (int i = 1; i <= j; ++i) {
- idx = new Index<K,V>(z, idx, null);
- if (i > h.level)
- h = new HeadIndex<K,V>(h.node, h, idx, i);
-
- if (i < preds.size()) {
- preds.get(i).right = idx;
- preds.set(i, idx);
- } else
- preds.add(idx);
- }
- }
- }
- head = h;
- }
-
- /* ------ Map API methods ------ */
-
- /**
- * Returns <tt>true</tt> if this map contains a mapping for the specified
- * key.
- *
- * @param key key whose presence in this map is to be tested
- * @return <tt>true</tt> if this map contains a mapping for the specified key
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key is null
- */
- public boolean containsKey(Object key) {
- return doGet(key) != null;
- }
-
- /**
- * Returns the value to which the specified key is mapped,
- * or {@code null} if this map contains no mapping for the key.
- *
- * <p>More formally, if this map contains a mapping from a key
- * {@code k} to a value {@code v} such that {@code key} compares
- * equal to {@code k} according to the map's ordering, then this
- * method returns {@code v}; otherwise it returns {@code null}.
- * (There can be at most one such mapping.)
- *
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key is null
- */
- public V get(Object key) {
- return doGet(key);
- }
-
- /**
- * Associates the specified value with the specified key in this map.
- * If the map previously contained a mapping for the key, the old
- * value is replaced.
- *
- * @param key key with which the specified value is to be associated
- * @param value value to be associated with the specified key
- * @return the previous value associated with the specified key, or
- * <tt>null</tt> if there was no mapping for the key
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key or value is null
- */
- public V put(K key, V value) {
- if (value == null)
- throw new NullPointerException();
- return doPut(key, value, false);
- }
-
- /**
- * Removes the mapping for the specified key from this map if present.
- *
- * @param key key for which mapping should be removed
- * @return the previous value associated with the specified key, or
- * <tt>null</tt> if there was no mapping for the key
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key is null
- */
- public V remove(Object key) {
- return doRemove(key, null);
- }
-
- /**
- * Returns <tt>true</tt> if this map maps one or more keys to the
- * specified value. This operation requires time linear in the
- * map size.
- *
- * @param value value whose presence in this map is to be tested
- * @return <tt>true</tt> if a mapping to <tt>value</tt> exists;
- * <tt>false</tt> otherwise
- * @throws NullPointerException if the specified value is null
- */
- public boolean containsValue(Object value) {
- if (value == null)
- throw new NullPointerException();
- for (Node<K,V> n = findFirst(); n != null; n = n.next) {
- V v = n.getValidValue();
- if (v != null && value.equals(v))
- return true;
- }
- return false;
- }
-
- /**
- * Returns the number of key-value mappings in this map. If this map
- * contains more than <tt>Integer.MAX_VALUE</tt> elements, it
- * returns <tt>Integer.MAX_VALUE</tt>.
- *
- * <p>Beware that, unlike in most collections, this method is
- * <em>NOT</em> a constant-time operation. Because of the
- * asynchronous nature of these maps, determining the current
- * number of elements requires traversing them all to count them.
- * Additionally, it is possible for the size to change during
- * execution of this method, in which case the returned result
- * will be inaccurate. Thus, this method is typically not very
- * useful in concurrent applications.
- *
- * @return the number of elements in this map
- */
- public int size() {
- long count = 0;
- for (Node<K,V> n = findFirst(); n != null; n = n.next) {
- if (n.getValidValue() != null)
- ++count;
- }
- return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count;
- }
-
- /**
- * 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() {
- return findFirst() == null;
- }
-
- /**
- * Removes all of the mappings from this map.
- */
- public void clear() {
- initialize();
- }
-
- /* ---------------- View methods -------------- */
-
- /*
- * Note: Lazy initialization works for views because view classes
- * are stateless/immutable so it doesn't matter wrt correctness if
- * more than one is created (which will only rarely happen). Even
- * so, the following idiom conservatively ensures that the method
- * returns the one it created if it does so, not one created by
- * another racing thread.
- */
-
- /**
- * Returns a {@link NavigableSet} view of the keys contained in this map.
- * The set's iterator returns the keys in ascending order.
- * 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 the map,
- * via the {@code Iterator.remove}, {@code Set.remove},
- * {@code removeAll}, {@code retainAll}, and {@code clear}
- * operations. It does not support the {@code add} or {@code addAll}
- * operations.
- *
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
- *
- * <p>This method is equivalent to method {@code navigableKeySet}.
- *
- * @return a navigable set view of the keys in this map
- */
- public NavigableSet<K> keySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
- }
-
- public NavigableSet<K> navigableKeySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
- }
-
- /**
- * Returns a {@link Collection} view of the values contained in this map.
- * The collection's iterator returns the values in ascending order
- * of the corresponding keys.
- * 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.
- *
- * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
- */
- public Collection<V> values() {
- Values vs = values;
- return (vs != null) ? vs : (values = new Values(this));
- }
-
- /**
- * Returns a {@link Set} view of the mappings contained in this map.
- * The set's iterator returns the entries in ascending key order.
- * 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 the 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.
- *
- * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
- *
- * <p>The <tt>Map.Entry</tt> elements returned by
- * <tt>iterator.next()</tt> do <em>not</em> support the
- * <tt>setValue</tt> operation.
- *
- * @return a set view of the mappings contained in this map,
- * sorted in ascending key order
- */
- public Set<Map.Entry<K,V>> entrySet() {
- EntrySet es = entrySet;
- return (es != null) ? es : (entrySet = new EntrySet(this));
- }
-
- public ConcurrentNavigableMap<K,V> descendingMap() {
- ConcurrentNavigableMap<K,V> dm = descendingMap;
- return (dm != null) ? dm : (descendingMap = new SubMap<K,V>
- (this, null, false, null, false, true));
- }
-
- public NavigableSet<K> descendingKeySet() {
- return descendingMap().navigableKeySet();
- }
-
- /* ---------------- AbstractMap Overrides -------------- */
-
- /**
- * Compares the specified object with this map for equality.
- * Returns <tt>true</tt> if the given object is also a map and the
- * two maps represent the same mappings. More formally, two maps
- * <tt>m1</tt> and <tt>m2</tt> represent the same mappings if
- * <tt>m1.entrySet().equals(m2.entrySet())</tt>. This
- * operation may return misleading results if either map is
- * concurrently modified during execution of this method.
- *
- * @param o object to be compared for equality with this map
- * @return <tt>true</tt> if the specified object is equal to this map
- */
- public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof Map))
- return false;
- Map<?,?> m = (Map<?,?>) o;
- try {
- for (Map.Entry<K,V> e : this.entrySet())
- if (! e.getValue().equals(m.get(e.getKey())))
- return false;
- for (Map.Entry<?,?> e : m.entrySet()) {
- Object k = e.getKey();
- Object v = e.getValue();
- if (k == null || v == null || !v.equals(get(k)))
- return false;
- }
- return true;
- } catch (ClassCastException unused) {
- return false;
- } catch (NullPointerException unused) {
- return false;
- }
- }
-
- /* ------ ConcurrentMap API methods ------ */
-
- /**
- * {@inheritDoc}
- *
- * @return the previous value associated with the specified key,
- * or <tt>null</tt> if there was no mapping for the key
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key or value is null
- */
- public V putIfAbsent(K key, V value) {
- if (value == null)
- throw new NullPointerException();
- return doPut(key, value, true);
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key is null
- */
- public boolean remove(Object key, Object value) {
- if (key == null)
- throw new NullPointerException();
- if (value == null)
- return false;
- return doRemove(key, value) != null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if any of the arguments are null
- */
- public boolean replace(K key, V oldValue, V newValue) {
- if (oldValue == null || newValue == null)
- throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
- for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
- return false;
- Object v = n.value;
- if (v != null) {
- if (!oldValue.equals(v))
- return false;
- if (n.casValue(v, newValue))
- return true;
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @return the previous value associated with the specified key,
- * or <tt>null</tt> if there was no mapping for the key
- * @throws ClassCastException if the specified key cannot be compared
- * with the keys currently in the map
- * @throws NullPointerException if the specified key or value is null
- */
- public V replace(K key, V value) {
- if (value == null)
- throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
- for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
- return null;
- Object v = n.value;
- if (v != null && n.casValue(v, value))
- return (V)v;
- }
- }
-
- /* ------ SortedMap API methods ------ */
-
- public Comparator<? super K> comparator() {
- return comparator;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public K firstKey() {
- Node<K,V> n = findFirst();
- if (n == null)
- throw new NoSuchElementException();
- return n.key;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public K lastKey() {
- Node<K,V> n = findLast();
- if (n == null)
- throw new NoSuchElementException();
- return n.key;
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code fromKey} or {@code toKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> subMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
- if (fromKey == null || toKey == null)
- throw new NullPointerException();
- return new SubMap<K,V>
- (this, fromKey, fromInclusive, toKey, toInclusive, false);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code toKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> headMap(K toKey,
- boolean inclusive) {
- if (toKey == null)
- throw new NullPointerException();
- return new SubMap<K,V>
- (this, null, false, toKey, inclusive, false);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code fromKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> tailMap(K fromKey,
- boolean inclusive) {
- if (fromKey == null)
- throw new NullPointerException();
- return new SubMap<K,V>
- (this, fromKey, inclusive, null, false, false);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code fromKey} or {@code toKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) {
- return subMap(fromKey, true, toKey, false);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code toKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> headMap(K toKey) {
- return headMap(toKey, false);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if {@code fromKey} is null
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public ConcurrentNavigableMap<K,V> tailMap(K fromKey) {
- return tailMap(fromKey, true);
- }
-
- /* ---------------- Relational operations -------------- */
-
- /**
- * Returns a key-value mapping associated with the greatest key
- * strictly less than the given key, or <tt>null</tt> if there is
- * no such key. The returned entry does <em>not</em> support the
- * <tt>Entry.setValue</tt> method.
- *
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public Map.Entry<K,V> lowerEntry(K key) {
- return getNear(key, LT);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public K lowerKey(K key) {
- Node<K,V> n = findNear(key, LT);
- return (n == null)? null : n.key;
- }
-
- /**
- * Returns a key-value mapping associated with the greatest key
- * less than or equal to the given key, or <tt>null</tt> if there
- * is no such key. The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- *
- * @param key the key
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public Map.Entry<K,V> floorEntry(K key) {
- return getNear(key, LT|EQ);
- }
-
- /**
- * @param key the key
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public K floorKey(K key) {
- Node<K,V> n = findNear(key, LT|EQ);
- return (n == null)? null : n.key;
- }
-
- /**
- * Returns a key-value mapping associated with the least key
- * greater than or equal to the given key, or <tt>null</tt> if
- * there is no such entry. The returned entry does <em>not</em>
- * support the <tt>Entry.setValue</tt> method.
- *
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public Map.Entry<K,V> ceilingEntry(K key) {
- return getNear(key, GT|EQ);
- }
-
- /**
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public K ceilingKey(K key) {
- Node<K,V> n = findNear(key, GT|EQ);
- return (n == null)? null : n.key;
- }
-
- /**
- * Returns a key-value mapping associated with the least key
- * strictly greater than the given key, or <tt>null</tt> if there
- * is no such key. The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- *
- * @param key the key
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public Map.Entry<K,V> higherEntry(K key) {
- return getNear(key, GT);
- }
-
- /**
- * @param key the key
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException if the specified key is null
- */
- public K higherKey(K key) {
- Node<K,V> n = findNear(key, GT);
- return (n == null)? null : n.key;
- }
-
- /**
- * Returns a key-value mapping associated with the least
- * key in this map, or <tt>null</tt> if the map is empty.
- * The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- */
- public Map.Entry<K,V> firstEntry() {
- for (;;) {
- Node<K,V> n = findFirst();
- if (n == null)
- return null;
- AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
- if (e != null)
- return e;
- }
- }
-
- /**
- * Returns a key-value mapping associated with the greatest
- * key in this map, or <tt>null</tt> if the map is empty.
- * The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- */
- public Map.Entry<K,V> lastEntry() {
- for (;;) {
- Node<K,V> n = findLast();
- if (n == null)
- return null;
- AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
- if (e != null)
- return e;
- }
- }
-
- /**
- * Removes and returns a key-value mapping associated with
- * the least key in this map, or <tt>null</tt> if the map is empty.
- * The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- */
- public Map.Entry<K,V> pollFirstEntry() {
- return doRemoveFirstEntry();
- }
-
- /**
- * Removes and returns a key-value mapping associated with
- * the greatest key in this map, or <tt>null</tt> if the map is empty.
- * The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
- */
- public Map.Entry<K,V> pollLastEntry() {
- return doRemoveLastEntry();
- }
-
-
- /* ---------------- Iterators -------------- */
-
- /**
- * Base of iterator classes:
- */
- abstract class Iter<T> implements Iterator<T> {
- /** the last node returned by next() */
- Node<K,V> lastReturned;
- /** the next node to return from next(); */
- Node<K,V> next;
- /** Cache of next value field to maintain weak consistency */
- V nextValue;
-
- /** Initializes ascending iterator for entire range. */
- Iter() {
- for (;;) {
- next = findFirst();
- if (next == null)
- break;
- Object x = next.value;
- if (x != null && x != next) {
- nextValue = (V) x;
- break;
- }
- }
- }
-
- public final boolean hasNext() {
- return next != null;
- }
-
- /** Advances next to higher entry. */
- final void advance() {
- if ((lastReturned = next) == null)
- throw new NoSuchElementException();
- for (;;) {
- next = next.next;
- if (next == null)
- break;
- Object x = next.value;
- if (x != null && x != next) {
- nextValue = (V) x;
- break;
- }
- }
- }
-
- public void remove() {
- Node<K,V> l = lastReturned;
- if (l == null)
- throw new IllegalStateException();
- // It would not be worth all of the overhead to directly
- // unlink from here. Using remove is fast enough.
- ConcurrentSkipListMap.this.remove(l.key);
- lastReturned = null;
- }
-
- }
-
- final class ValueIterator extends Iter<V> {
- public V next() {
- V v = nextValue;
- advance();
- return v;
- }
- }
-
- final class KeyIterator extends Iter<K> {
- public K next() {
- Node<K,V> n = next;
- advance();
- return n.key;
- }
- }
-
- final class EntryIterator extends Iter<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() {
- Node<K,V> n = next;
- V v = nextValue;
- advance();
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
- }
- }
-
- // Factory methods for iterators needed by ConcurrentSkipListSet etc
-
- Iterator<K> keyIterator() {
- return new KeyIterator();
- }
-
- Iterator<V> valueIterator() {
- return new ValueIterator();
- }
-
- Iterator<Map.Entry<K,V>> entryIterator() {
- return new EntryIterator();
- }
-
- /* ---------------- View Classes -------------- */
-
- /*
- * View classes are static, delegating to a ConcurrentNavigableMap
- * to allow use by SubMaps, which outweighs the ugliness of
- * needing type-tests for Iterator methods.
- */
-
- static final <E> List<E> toList(Collection<E> c) {
- // Using size() here would be a pessimization.
- List<E> list = new ArrayList<E>();
- for (E e : c)
- list.add(e);
- return list;
- }
-
- static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
- private final ConcurrentNavigableMap<E,Object> m;
- KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; }
- public int size() { return m.size(); }
- public boolean isEmpty() { return m.isEmpty(); }
- public boolean contains(Object o) { return m.containsKey(o); }
- public boolean remove(Object o) { return m.remove(o) != null; }
- public void clear() { m.clear(); }
- public E lower(E e) { return m.lowerKey(e); }
- public E floor(E e) { return m.floorKey(e); }
- public E ceiling(E e) { return m.ceilingKey(e); }
- public E higher(E e) { return m.higherKey(e); }
- public Comparator<? super E> comparator() { return m.comparator(); }
- public E first() { return m.firstKey(); }
- public E last() { return m.lastKey(); }
- public E pollFirst() {
- Map.Entry<E,Object> e = m.pollFirstEntry();
- return e == null? null : e.getKey();
- }
- public E pollLast() {
- Map.Entry<E,Object> e = m.pollLastEntry();
- return e == null? null : e.getKey();
- }
- public Iterator<E> iterator() {
- if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<E,Object>)m).keyIterator();
- else
- return ((ConcurrentSkipListMap.SubMap<E,Object>)m).keyIterator();
- }
- public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof Set))
- return false;
- Collection<?> c = (Collection<?>) o;
- try {
- return containsAll(c) && c.containsAll(this);
- } catch (ClassCastException unused) {
- return false;
- } catch (NullPointerException unused) {
- return false;
- }
- }
- public Object[] toArray() { return toList(this).toArray(); }
- public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
- public Iterator<E> descendingIterator() {
- return descendingSet().iterator();
- }
- public NavigableSet<E> subSet(E fromElement,
- boolean fromInclusive,
- E toElement,
- boolean toInclusive) {
- return new ConcurrentSkipListSet<E>
- (m.subMap(fromElement, fromInclusive,
- toElement, toInclusive));
- }
- public NavigableSet<E> headSet(E toElement, boolean inclusive) {
- return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
- }
- public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
- return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
- }
- public NavigableSet<E> subSet(E fromElement, E toElement) {
- return subSet(fromElement, true, toElement, false);
- }
- public NavigableSet<E> headSet(E toElement) {
- return headSet(toElement, false);
- }
- public NavigableSet<E> tailSet(E fromElement) {
- return tailSet(fromElement, true);
- }
- public NavigableSet<E> descendingSet() {
- return new ConcurrentSkipListSet(m.descendingMap());
- }
- }
-
- static final class Values<E> extends AbstractCollection<E> {
- private final ConcurrentNavigableMap<Object, E> m;
- Values(ConcurrentNavigableMap<Object, E> map) {
- m = map;
- }
- public Iterator<E> iterator() {
- if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<Object,E>)m).valueIterator();
- else
- return ((SubMap<Object,E>)m).valueIterator();
- }
- public boolean isEmpty() {
- return m.isEmpty();
- }
- public int size() {
- return m.size();
- }
- public boolean contains(Object o) {
- return m.containsValue(o);
- }
- public void clear() {
- m.clear();
- }
- public Object[] toArray() { return toList(this).toArray(); }
- public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
- }
-
- static final class EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> {
- private final ConcurrentNavigableMap<K1, V1> m;
- EntrySet(ConcurrentNavigableMap<K1, V1> map) {
- m = map;
- }
-
- public Iterator<Map.Entry<K1,V1>> iterator() {
- if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<K1,V1>)m).entryIterator();
- else
- return ((SubMap<K1,V1>)m).entryIterator();
- }
-
- public boolean contains(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
- V1 v = m.get(e.getKey());
- return v != null && v.equals(e.getValue());
- }
- public boolean remove(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
- return m.remove(e.getKey(),
- e.getValue());
- }
- public boolean isEmpty() {
- return m.isEmpty();
- }
- public int size() {
- return m.size();
- }
- public void clear() {
- m.clear();
- }
- public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof Set))
- return false;
- Collection<?> c = (Collection<?>) o;
- try {
- return containsAll(c) && c.containsAll(this);
- } catch (ClassCastException unused) {
- return false;
- } catch (NullPointerException unused) {
- return false;
- }
- }
- public Object[] toArray() { return toList(this).toArray(); }
- public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
- }
-
- /**
- * Submaps returned by {@link ConcurrentSkipListMap} submap operations
- * represent a subrange of mappings of their underlying
- * maps. Instances of this class support all methods of their
- * underlying maps, differing in that mappings outside their range are
- * ignored, and attempts to add mappings outside their ranges result
- * in {@link IllegalArgumentException}. Instances of this class are
- * constructed only using the <tt>subMap</tt>, <tt>headMap</tt>, and
- * <tt>tailMap</tt> methods of their underlying maps.
- *
- * @serial include
- */
- static final class SubMap<K,V> extends AbstractMap<K,V>
- implements ConcurrentNavigableMap<K,V>, Cloneable,
- java.io.Serializable {
- private static final long serialVersionUID = -7647078645895051609L;
-
- /** Underlying map */
- private final ConcurrentSkipListMap<K,V> m;
- /** lower bound key, or null if from start */
- private final K lo;
- /** upper bound key, or null if to end */
- private final K hi;
- /** inclusion flag for lo */
- private final boolean loInclusive;
- /** inclusion flag for hi */
- private final boolean hiInclusive;
- /** direction */
- private final boolean isDescending;
-
- // Lazily initialized view holders
- private transient KeySet<K> keySetView;
- private transient Set<Map.Entry<K,V>> entrySetView;
- private transient Collection<V> valuesView;
-
- /**
- * Creates a new submap, initializing all fields
- */
- SubMap(ConcurrentSkipListMap<K,V> map,
- K fromKey, boolean fromInclusive,
- K toKey, boolean toInclusive,
- boolean isDescending) {
- if (fromKey != null && toKey != null &&
- map.compare(fromKey, toKey) > 0)
- throw new IllegalArgumentException("inconsistent range");
- this.m = map;
- this.lo = fromKey;
- this.hi = toKey;
- this.loInclusive = fromInclusive;
- this.hiInclusive = toInclusive;
- this.isDescending = isDescending;
- }
-
- /* ---------------- Utilities -------------- */
-
- private boolean tooLow(K key) {
- if (lo != null) {
- int c = m.compare(key, lo);
- if (c < 0 || (c == 0 && !loInclusive))
- return true;
- }
- return false;
- }
-
- private boolean tooHigh(K key) {
- if (hi != null) {
- int c = m.compare(key, hi);
- if (c > 0 || (c == 0 && !hiInclusive))
- return true;
- }
- return false;
- }
-
- private boolean inBounds(K key) {
- return !tooLow(key) && !tooHigh(key);
- }
-
- private void checkKeyBounds(K key) throws IllegalArgumentException {
- if (key == null)
- throw new NullPointerException();
- if (!inBounds(key))
- throw new IllegalArgumentException("key out of range");
- }
-
- /**
- * Returns true if node key is less than upper bound of range
- */
- private boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n) {
- if (n == null)
- return false;
- if (hi == null)
- return true;
- K k = n.key;
- if (k == null) // pass by markers and headers
- return true;
- int c = m.compare(k, hi);
- if (c > 0 || (c == 0 && !hiInclusive))
- return false;
- return true;
- }
-
- /**
- * Returns lowest node. This node might not be in range, so
- * most usages need to check bounds
- */
- private ConcurrentSkipListMap.Node<K,V> loNode() {
- if (lo == null)
- return m.findFirst();
- else if (loInclusive)
- return m.findNear(lo, m.GT|m.EQ);
- else
- return m.findNear(lo, m.GT);
- }
-
- /**
- * Returns highest node. This node might not be in range, so
- * most usages need to check bounds
- */
- private ConcurrentSkipListMap.Node<K,V> hiNode() {
- if (hi == null)
- return m.findLast();
- else if (hiInclusive)
- return m.findNear(hi, m.LT|m.EQ);
- else
- return m.findNear(hi, m.LT);
- }
-
- /**
- * Returns lowest absolute key (ignoring directonality)
- */
- private K lowestKey() {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
- return n.key;
- else
- throw new NoSuchElementException();
- }
-
- /**
- * Returns highest absolute key (ignoring directonality)
- */
- private K highestKey() {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
- if (n != null) {
- K last = n.key;
- if (inBounds(last))
- return last;
- }
- throw new NoSuchElementException();
- }
-
- private Map.Entry<K,V> lowestEntry() {
- for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (!isBeforeEnd(n))
- return null;
- Map.Entry<K,V> e = n.createSnapshot();
- if (e != null)
- return e;
- }
- }
-
- private Map.Entry<K,V> highestEntry() {
- for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
- if (n == null || !inBounds(n.key))
- return null;
- Map.Entry<K,V> e = n.createSnapshot();
- if (e != null)
- return e;
- }
- }
-
- private Map.Entry<K,V> removeLowest() {
- for (;;) {
- Node<K,V> n = loNode();
- if (n == null)
- return null;
- K k = n.key;
- if (!inBounds(k))
- return null;
- V v = m.doRemove(k, null);
- if (v != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
- }
- }
-
- private Map.Entry<K,V> removeHighest() {
- for (;;) {
- Node<K,V> n = hiNode();
- if (n == null)
- return null;
- K k = n.key;
- if (!inBounds(k))
- return null;
- V v = m.doRemove(k, null);
- if (v != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
- }
- }
-
- /**
- * Submap version of ConcurrentSkipListMap.getNearEntry
- */
- private Map.Entry<K,V> getNearEntry(K key, int rel) {
- if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
- else
- rel &= ~m.LT;
- }
- if (tooLow(key))
- return ((rel & m.LT) != 0)? null : lowestEntry();
- if (tooHigh(key))
- return ((rel & m.LT) != 0)? highestEntry() : null;
- for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
- return null;
- K k = n.key;
- V v = n.getValidValue();
- if (v != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
- }
- }
-
- // Almost the same as getNearEntry, except for keys
- private K getNearKey(K key, int rel) {
- if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
- else
- rel &= ~m.LT;
- }
- if (tooLow(key)) {
- if ((rel & m.LT) == 0) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
- return n.key;
- }
- return null;
- }
- if (tooHigh(key)) {
- if ((rel & m.LT) != 0) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
- if (n != null) {
- K last = n.key;
- if (inBounds(last))
- return last;
- }
- }
- return null;
- }
- for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
- return null;
- K k = n.key;
- V v = n.getValidValue();
- if (v != null)
- return k;
- }
- }
-
- /* ---------------- Map API methods -------------- */
-
- public boolean containsKey(Object key) {
- if (key == null) throw new NullPointerException();
- K k = (K)key;
- return inBounds(k) && m.containsKey(k);
- }
-
- public V get(Object key) {
- if (key == null) throw new NullPointerException();
- K k = (K)key;
- return ((!inBounds(k)) ? null : m.get(k));
- }
-
- public V put(K key, V value) {
- checkKeyBounds(key);
- return m.put(key, value);
- }
-
- public V remove(Object key) {
- K k = (K)key;
- return (!inBounds(k))? null : m.remove(k);
- }
-
- public int size() {
- long count = 0;
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
- n = n.next) {
- if (n.getValidValue() != null)
- ++count;
- }
- return count >= Integer.MAX_VALUE? Integer.MAX_VALUE : (int)count;
- }
-
- public boolean isEmpty() {
- return !isBeforeEnd(loNode());
- }
-
- public boolean containsValue(Object value) {
- if (value == null)
- throw new NullPointerException();
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
- n = n.next) {
- V v = n.getValidValue();
- if (v != null && value.equals(v))
- return true;
- }
- return false;
- }
-
- public void clear() {
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
- n = n.next) {
- if (n.getValidValue() != null)
- m.remove(n.key);
- }
- }
-
- /* ---------------- ConcurrentMap API methods -------------- */
-
- public V putIfAbsent(K key, V value) {
- checkKeyBounds(key);
- return m.putIfAbsent(key, value);
- }
-
- public boolean remove(Object key, Object value) {
- K k = (K)key;
- return inBounds(k) && m.remove(k, value);
- }
-
- public boolean replace(K key, V oldValue, V newValue) {
- checkKeyBounds(key);
- return m.replace(key, oldValue, newValue);
- }
-
- public V replace(K key, V value) {
- checkKeyBounds(key);
- return m.replace(key, value);
- }
-
- /* ---------------- SortedMap API methods -------------- */
-
- public Comparator<? super K> comparator() {
- Comparator<? super K> cmp = m.comparator();
- if (isDescending)
- return Collections.reverseOrder(cmp);
- else
- return cmp;
- }
-
- /**
- * Utility to create submaps, where given bounds override
- * unbounded(null) ones and/or are checked against bounded ones.
- */
- private SubMap<K,V> newSubMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
- if (isDescending) { // flip senses
- K tk = fromKey;
- fromKey = toKey;
- toKey = tk;
- boolean ti = fromInclusive;
- fromInclusive = toInclusive;
- toInclusive = ti;
- }
- if (lo != null) {
- if (fromKey == null) {
- fromKey = lo;
- fromInclusive = loInclusive;
- }
- else {
- int c = m.compare(fromKey, lo);
- if (c < 0 || (c == 0 && !loInclusive && fromInclusive))
- throw new IllegalArgumentException("key out of range");
- }
- }
- if (hi != null) {
- if (toKey == null) {
- toKey = hi;
- toInclusive = hiInclusive;
- }
- else {
- int c = m.compare(toKey, hi);
- if (c > 0 || (c == 0 && !hiInclusive && toInclusive))
- throw new IllegalArgumentException("key out of range");
- }
- }
- return new SubMap<K,V>(m, fromKey, fromInclusive,
- toKey, toInclusive, isDescending);
- }
-
- public SubMap<K,V> subMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
- if (fromKey == null || toKey == null)
- throw new NullPointerException();
- return newSubMap(fromKey, fromInclusive, toKey, toInclusive);
- }
-
- public SubMap<K,V> headMap(K toKey,
- boolean inclusive) {
- if (toKey == null)
- throw new NullPointerException();
- return newSubMap(null, false, toKey, inclusive);
- }
-
- public SubMap<K,V> tailMap(K fromKey,
- boolean inclusive) {
- if (fromKey == null)
- throw new NullPointerException();
- return newSubMap(fromKey, inclusive, null, false);
- }
-
- public SubMap<K,V> subMap(K fromKey, K toKey) {
- return subMap(fromKey, true, toKey, false);
- }
-
- public SubMap<K,V> headMap(K toKey) {
- return headMap(toKey, false);
- }
-
- public SubMap<K,V> tailMap(K fromKey) {
- return tailMap(fromKey, true);
- }
-
- public SubMap<K,V> descendingMap() {
- return new SubMap<K,V>(m, lo, loInclusive,
- hi, hiInclusive, !isDescending);
- }
-
- /* ---------------- Relational methods -------------- */
-
- public Map.Entry<K,V> ceilingEntry(K key) {
- return getNearEntry(key, (m.GT|m.EQ));
- }
-
- public K ceilingKey(K key) {
- return getNearKey(key, (m.GT|m.EQ));
- }
-
- public Map.Entry<K,V> lowerEntry(K key) {
- return getNearEntry(key, (m.LT));
- }
-
- public K lowerKey(K key) {
- return getNearKey(key, (m.LT));
- }
-
- public Map.Entry<K,V> floorEntry(K key) {
- return getNearEntry(key, (m.LT|m.EQ));
- }
-
- public K floorKey(K key) {
- return getNearKey(key, (m.LT|m.EQ));
- }
-
- public Map.Entry<K,V> higherEntry(K key) {
- return getNearEntry(key, (m.GT));
- }
-
- public K higherKey(K key) {
- return getNearKey(key, (m.GT));
- }
-
- public K firstKey() {
- return isDescending? highestKey() : lowestKey();
- }
-
- public K lastKey() {
- return isDescending? lowestKey() : highestKey();
- }
-
- public Map.Entry<K,V> firstEntry() {
- return isDescending? highestEntry() : lowestEntry();
- }
-
- public Map.Entry<K,V> lastEntry() {
- return isDescending? lowestEntry() : highestEntry();
- }
-
- public Map.Entry<K,V> pollFirstEntry() {
- return isDescending? removeHighest() : removeLowest();
- }
-
- public Map.Entry<K,V> pollLastEntry() {
- return isDescending? removeLowest() : removeHighest();
- }
-
- /* ---------------- Submap Views -------------- */
-
- public NavigableSet<K> keySet() {
- KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
- }
-
- public NavigableSet<K> navigableKeySet() {
- KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
- }
-
- public Collection<V> values() {
- Collection<V> vs = valuesView;
- return (vs != null) ? vs : (valuesView = new Values(this));
- }
-
- public Set<Map.Entry<K,V>> entrySet() {
- Set<Map.Entry<K,V>> es = entrySetView;
- return (es != null) ? es : (entrySetView = new EntrySet(this));
- }
-
- public NavigableSet<K> descendingKeySet() {
- return descendingMap().navigableKeySet();
- }
-
- Iterator<K> keyIterator() {
- return new SubMapKeyIterator();
- }
-
- Iterator<V> valueIterator() {
- return new SubMapValueIterator();
- }
-
- Iterator<Map.Entry<K,V>> entryIterator() {
- return new SubMapEntryIterator();
- }
-
- /**
- * Variant of main Iter class to traverse through submaps.
- */
- abstract class SubMapIter<T> implements Iterator<T> {
- /** the last node returned by next() */
- Node<K,V> lastReturned;
- /** the next node to return from next(); */
- Node<K,V> next;
- /** Cache of next value field to maintain weak consistency */
- V nextValue;
-
- SubMapIter() {
- for (;;) {
- next = isDescending ? hiNode() : loNode();
- if (next == null)
- break;
- Object x = next.value;
- if (x != null && x != next) {
- if (! inBounds(next.key))
- next = null;
- else
- nextValue = (V) x;
- break;
- }
- }
- }
-
- public final boolean hasNext() {
- return next != null;
- }
-
- final void advance() {
- if ((lastReturned = next) == null)
- throw new NoSuchElementException();
- if (isDescending)
- descend();
- else
- ascend();
- }
-
- private void ascend() {
- for (;;) {
- next = next.next;
- if (next == null)
- break;
- Object x = next.value;
- if (x != null && x != next) {
- if (tooHigh(next.key))
- next = null;
- else
- nextValue = (V) x;
- break;
- }
- }
- }
-
- private void descend() {
- for (;;) {
- next = m.findNear(lastReturned.key, LT);
- if (next == null)
- break;
- Object x = next.value;
- if (x != null && x != next) {
- if (tooLow(next.key))
- next = null;
- else
- nextValue = (V) x;
- break;
- }
- }
- }
-
- public void remove() {
- Node<K,V> l = lastReturned;
- if (l == null)
- throw new IllegalStateException();
- m.remove(l.key);
- lastReturned = null;
- }
-
- }
-
- final class SubMapValueIterator extends SubMapIter<V> {
- public V next() {
- V v = nextValue;
- advance();
- return v;
- }
- }
-
- final class SubMapKeyIterator extends SubMapIter<K> {
- public K next() {
- Node<K,V> n = next;
- advance();
- return n.key;
- }
- }
-
- final class SubMapEntryIterator extends SubMapIter<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() {
- Node<K,V> n = next;
- V v = nextValue;
- advance();
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
- }
- }
- }
-}