--- jsr166/src/jsr166x/ConcurrentSkipListMap.java 2004/10/16 14:49:45 1.4
+++ jsr166/src/jsr166x/ConcurrentSkipListMap.java 2013/02/18 03:15:10 1.31
@@ -1,10 +1,10 @@
/*
* 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
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
-package jsr166x;
+package jsr166x;
import java.util.*;
import java.util.concurrent.*;
@@ -20,46 +20,47 @@ import java.util.concurrent.atomic.*;
*
This class implements a concurrent variant of SkipLists providing
* expected average log(n) time cost for the
- * containsKey, get, put and
- * remove operations and their variants. Insertion, removal,
+ * {@code containsKey}, {@code get}, {@code put} and
+ * {@code remove} operations and their variants. Insertion, removal,
* update, and access operations safely execute concurrently by
* multiple threads. Iterators are weakly consistent, returning
* elements reflecting the state of the map at some point at or since
* the creation of the iterator. They do not throw {@link
* ConcurrentModificationException}, and may proceed concurrently with
- * other operations.
+ * other operations. Ascending key ordered views and their iterators
+ * are faster than descending ones.
*
- *
All Map.Entry pairs returned by methods in this class
+ *
All {@code Map.Entry} pairs returned by methods in this class
* and its views represent snapshots of mappings at the time they were
- * produced. They do not support the Entry.setValue
+ * produced. They do not support the {@code Entry.setValue}
* method. (Note however that it is possible to change mappings in the
- * associated map using put, putIfAbsent, or
- * replace, depending on exactly which effect you need.)
+ * associated map using {@code put}, {@code putIfAbsent}, or
+ * {@code replace}, depending on exactly which effect you need.)
*
- *
Beware that, unlike in most collections, the size
+ *
Beware that, unlike in most collections, the {@code size}
* method is not 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 putAll, equals, and
- * clear are not guaranteed to be performed
+ * the bulk operations {@code putAll}, {@code equals}, and
+ * {@code clear} are not guaranteed to be performed
* atomically. For example, an iterator operating concurrently with a
- * putAll operation might view only some of the added
+ * {@code putAll} operation might view only some of the added
* elements.
*
*
This class and its views and iterators implement all of the
* optional methods of the {@link Map} and {@link Iterator}
* interfaces. Like most other concurrent collections, this class does
- * not permit the use of null keys or values because some
+ * not permit the use of {@code null} keys or values because some
* null return values cannot be reliably distinguished from the
* absence of elements.
*
* @author Doug Lea
* @param the type of keys maintained by this map
- * @param the type of mapped values
+ * @param the type of mapped values
*/
-public class ConcurrentSkipListMap extends AbstractMap
+public class ConcurrentSkipListMap extends AbstractMap
implements ConcurrentNavigableMap,
- Cloneable,
+ Cloneable,
java.io.Serializable {
/*
* This class implements a tree-like two-dimensionally linked skip
@@ -73,19 +74,19 @@ public class ConcurrentSkipListMap
* possible list with 2 levels of index:
*
* Head nodes Index nodes
- * +-+ right +-+ +-+
+ * +-+ 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
@@ -144,9 +145,9 @@ public class ConcurrentSkipListMap
* 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
@@ -160,15 +161,15 @@ public class ConcurrentSkipListMap
*
* +------+ +------+ +------+ +------+
* ... | 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
@@ -187,7 +188,7 @@ public class ConcurrentSkipListMap
* 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.
+ * 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
@@ -265,8 +266,8 @@ public class ConcurrentSkipListMap
* 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 papers by
- * Håkan Sundell (http://www.cs.chalmers.se/~phs/).
+ * (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
@@ -291,11 +292,11 @@ public class ConcurrentSkipListMap
/**
* Special value used to identify base-level header
- */
+ */
private static final Object BASE_HEADER = new Object();
/**
- * The topmost head index of the skiplist.
+ * The topmost head index of the skiplist.
*/
private transient volatile HeadIndex head;
@@ -318,24 +319,30 @@ public class ConcurrentSkipListMap
private transient EntrySet entrySet;
/** Lazily initialized values collection */
private transient Values values;
+ /** Lazily initialized descending key set */
+ private transient DescendingKeySet descendingKeySet;
+ /** Lazily initialized descending entry set */
+ private transient DescendingEntrySet descendingEntrySet;
/**
- * Initialize or reset state. Needed by constructors, clone,
+ * 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;
+ entrySet = null;
values = null;
+ descendingEntrySet = null;
+ descendingKeySet = null;
randomSeed = (int) System.nanoTime();
head = new HeadIndex(new Node(null, BASE_HEADER, null),
null, null, 1);
}
/** Updater for casHead */
- private static final
- AtomicReferenceFieldUpdater
+ private static final
+ AtomicReferenceFieldUpdater
headUpdater = AtomicReferenceFieldUpdater.newUpdater
(ConcurrentSkipListMap.class, HeadIndex.class, "head");
@@ -383,16 +390,15 @@ public class ConcurrentSkipListMap
}
/** Updater for casNext */
- static final AtomicReferenceFieldUpdater
+ static final AtomicReferenceFieldUpdater
nextUpdater = AtomicReferenceFieldUpdater.newUpdater
(Node.class, Node.class, "next");
/** Updater for casValue */
- static final AtomicReferenceFieldUpdater
+ static final AtomicReferenceFieldUpdater
valueUpdater = AtomicReferenceFieldUpdater.newUpdater
(Node.class, Object.class, "value");
-
/**
* compareAndSet value field
*/
@@ -408,12 +414,12 @@ public class ConcurrentSkipListMap
}
/**
- * Return true if this node is a marker. This method isn't
+ * Returns true if this node is a marker. This method isn't
* actually called in an 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() {
@@ -421,7 +427,7 @@ public class ConcurrentSkipListMap
}
/**
- * Return true if this node is the header of base-level list.
+ * Returns true if this node is the header of base-level list.
* @return true if this node is header node
*/
boolean isBaseHeader() {
@@ -459,10 +465,10 @@ public class ConcurrentSkipListMap
}
/**
- * Return value if this node contains a valid key-value pair,
- * else null.
+ * 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.
+ * is deleted, else null
*/
V getValidValue() {
Object v = value;
@@ -472,8 +478,8 @@ public class ConcurrentSkipListMap
}
/**
- * Create and return a new SnapshotEntry holding current
- * mapping if this node holds a valid value, else null
+ * Creates and returns a new SnapshotEntry holding current
+ * mapping if this node holds a valid value, else null.
* @return new entry or null
*/
SnapshotEntry createSnapshot() {
@@ -501,8 +507,8 @@ public class ConcurrentSkipListMap
volatile Index right;
/**
- * Creates index node with given values
- */
+ * Creates index node with given values.
+ */
Index(Node node, Index down, Index right) {
this.node = node;
this.key = node.key;
@@ -511,7 +517,7 @@ public class ConcurrentSkipListMap
}
/** Updater for casRight */
- static final AtomicReferenceFieldUpdater
+ static final AtomicReferenceFieldUpdater
rightUpdater = AtomicReferenceFieldUpdater.newUpdater
(Index.class, Index.class, "right");
@@ -540,7 +546,7 @@ public class ConcurrentSkipListMap
*/
final boolean link(Index succ, Index newSucc) {
Node n = node;
- newSucc.right = succ;
+ newSucc.right = succ;
return n.value != null && casRight(succ, newSucc);
}
@@ -567,18 +573,18 @@ public class ConcurrentSkipListMap
super(node, down, right);
this.level = level;
}
- }
+ }
/* ---------------- Map.Entry support -------------- */
/**
* An immutable representation of a key-value mapping as it
* existed at some point in time. This class does not
- * support the Map.Entry.setValue method.
- */
+ * support the {@code Map.Entry.setValue} method.
+ */
static class SnapshotEntry implements Map.Entry {
- private final K key;
- private final V value;
+ private final K key;
+ private final V value;
/**
* Creates a new entry representing the given key and value.
@@ -586,31 +592,31 @@ public class ConcurrentSkipListMap
* @param value the value
*/
SnapshotEntry(K key, V value) {
- this.key = key;
- this.value = value;
- }
-
- /**
- * Returns the key corresponding to this entry.
- *
- * @return the key corresponding to this entry.
- */
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * Returns the key corresponding to this entry.
+ *
+ * @return the key corresponding to this entry
+ */
public K getKey() {
return key;
}
- /**
- * Returns the value corresponding to this entry.
- *
- * @return the value corresponding to this entry.
- */
+ /**
+ * Returns the value corresponding to this entry.
+ *
+ * @return the value corresponding to this entry
+ */
public V getValue() {
- return value;
+ return value;
}
- /**
- * Always fails, throwing UnsupportedOperationException.
- * @throws UnsupportedOperationException always.
+ /**
+ * Always fails, throwing {@code UnsupportedOperationException}.
+ * @throws UnsupportedOperationException always
*/
public V setValue(V value) {
throw new UnsupportedOperationException();
@@ -638,12 +644,12 @@ public class ConcurrentSkipListMap
/**
* Returns a String consisting of the key followed by an
- * equals sign ("=") followed by the associated
+ * equals sign ({@code "="}) followed by the associated
* value.
- * @return a String representation of this entry.
+ * @return a String representation of this entry
*/
public String toString() {
- return getKey() + "=" + getValue();
+ return getKey() + "=" + getValue();
}
}
@@ -682,15 +688,15 @@ public class ConcurrentSkipListMap
* which is propagated back to caller.
*/
private Comparable comparable(Object key) throws ClassCastException {
- if (key == null)
+ if (key == null)
throw new NullPointerException();
- return (comparator != null)
- ? new ComparableUsingComparator(key, comparator)
+ return (comparator != null)
+ ? new ComparableUsingComparator(key, comparator)
: (Comparable)key;
}
/**
- * Compare using comparator or natural ordering. Used when the
+ * Compares using comparator or natural ordering. Used when the
* ComparableUsingComparator approach doesn't apply.
*/
int compare(K k1, K k2) throws ClassCastException {
@@ -702,23 +708,23 @@ public class ConcurrentSkipListMap
}
/**
- * Return true if given key greater than or equal to least and
+ * Returns true if given key greater than or equal to least and
* strictly less than fence, bypassing either test if least or
- * fence oare null. Needed mainly in submap operations.
+ * fence are null. Needed mainly in submap operations.
*/
boolean inHalfOpenRange(K key, K least, K fence) {
- if (key == null)
+ if (key == null)
throw new NullPointerException();
return ((least == null || compare(key, least) >= 0) &&
(fence == null || compare(key, fence) < 0));
}
/**
- * Return true if given key greater than or equal to least and less
+ * 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)
+ if (key == null)
throw new NullPointerException();
return ((least == null || compare(key, least) >= 0) &&
(fence == null || compare(key, fence) <= 0));
@@ -727,12 +733,12 @@ public class ConcurrentSkipListMap
/* ---------------- Traversal -------------- */
/**
- * Return a base-level node with key strictly less than given key,
+ * 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
+ * @return a predecessor of key
*/
private Node findPredecessor(Comparable key) {
for (;;) {
@@ -751,7 +757,7 @@ public class ConcurrentSkipListMap
continue;
}
}
- if ((d = q.down) != null)
+ if ((d = q.down) != null)
q = d;
else
return q.node;
@@ -760,7 +766,7 @@ public class ConcurrentSkipListMap
}
/**
- * Return node holding key or null if no such, clearing out any
+ * 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
@@ -781,7 +787,7 @@ public class ConcurrentSkipListMap
* 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,
+ * (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
@@ -799,16 +805,16 @@ public class ConcurrentSkipListMap
* findLast. 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.
+ * @return node holding key, or null if no such
*/
private Node findNode(Comparable key) {
for (;;) {
Node b = findPredecessor(key);
Node n = b.next;
for (;;) {
- if (n == null)
+ if (n == null)
return null;
Node f = n.next;
if (n != b.next) // inconsistent read
@@ -823,7 +829,7 @@ public class ConcurrentSkipListMap
int c = key.compareTo(n.key);
if (c < 0)
return null;
- if (c == 0)
+ if (c == 0)
return n;
b = n;
n = f;
@@ -831,8 +837,8 @@ public class ConcurrentSkipListMap
}
}
- /**
- * Specialized variant of findNode to perform map.get. Does a weak
+ /**
+ * 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
@@ -850,7 +856,7 @@ public class ConcurrentSkipListMap
for (;;) {
K rk;
Index d, r;
- if ((r = q.right) != null &&
+ if ((r = q.right) != null &&
(rk = r.key) != null && rk != bound) {
int c = key.compareTo(rk);
if (c > 0) {
@@ -859,11 +865,11 @@ public class ConcurrentSkipListMap
}
if (c == 0) {
Object v = r.node.value;
- return (v != null)? (V)v : getUsingFindNode(key);
+ return (v != null) ? (V)v : getUsingFindNode(key);
}
bound = rk;
}
- if ((d = q.down) != null)
+ if ((d = q.down) != null)
q = d;
else {
for (Node n = q.node.next; n != null; n = n.next) {
@@ -872,7 +878,7 @@ public class ConcurrentSkipListMap
int c = key.compareTo(nk);
if (c == 0) {
Object v = n.value;
- return (v != null)? (V)v : getUsingFindNode(key);
+ return (v != null) ? (V)v : getUsingFindNode(key);
}
if (c < 0)
return null;
@@ -884,7 +890,7 @@ public class ConcurrentSkipListMap
}
/**
- * Perform map.get via findNode. Used as a backup if doGet
+ * 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
@@ -910,8 +916,8 @@ public class ConcurrentSkipListMap
/**
* 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 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
*/
@@ -924,7 +930,7 @@ public class ConcurrentSkipListMap
if (n != null) {
Node f = n.next;
if (n != b.next) // inconsistent read
- break;;
+ break;
Object v = n.value;
if (v == null) { // n is deleted
n.helpDelete(b, f);
@@ -946,12 +952,12 @@ public class ConcurrentSkipListMap
}
// else c < 0; fall through
}
-
+
Node z = new Node(kkey, value, n);
- if (!b.casNext(n, z))
+ if (!b.casNext(n, z))
break; // restart if lost race to append to b
- int level = randomLevel();
- if (level > 0)
+ int level = randomLevel();
+ if (level > 0)
insertIndex(z, level);
return null;
}
@@ -959,7 +965,7 @@ public class ConcurrentSkipListMap
}
/**
- * Return a random level for inserting a new node.
+ * Returns a random level for inserting a new node.
* Hardwired to k=1, p=0.5, max 31.
*
* This uses a cheap pseudo-random function that according to
@@ -973,15 +979,15 @@ public class ConcurrentSkipListMap
int level = 0;
int r = randomSeed;
randomSeed = r * 134775813 + 1;
- if (r < 0) {
- while ((r <<= 1) > 0)
+ if (r < 0) {
+ while ((r <<= 1) > 0)
++level;
}
return level;
}
/**
- * Create and add index nodes for given node.
+ * Creates and adds index nodes for given node.
* @param z the node
* @param level the level of the index
*/
@@ -1007,7 +1013,7 @@ public class ConcurrentSkipListMap
level = max + 1;
Index[] idxs = (Index[])new Index[level+1];
Index idx = null;
- for (int i = 1; i <= level; ++i)
+ for (int i = 1; i <= level; ++i)
idxs[i] = idx = new Index(z, idx, null);
HeadIndex oldh;
@@ -1021,7 +1027,7 @@ public class ConcurrentSkipListMap
}
HeadIndex newh = oldh;
Node oldbase = oldh.node;
- for (int j = oldLevel+1; j <= level; ++j)
+ for (int j = oldLevel+1; j <= level; ++j)
newh = new HeadIndex(oldbase, newh, idxs[j], j);
if (casHead(oldh, newh)) {
k = oldLevel;
@@ -1033,10 +1039,10 @@ public class ConcurrentSkipListMap
}
/**
- * Add given index nodes from given level down to 1.
+ * 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
+ * snapshotted by callers to provide correct insertion level.
* @param indexLevel the level of the index
*/
private void addIndex(Index idx, HeadIndex h, int indexLevel) {
@@ -1059,7 +1065,7 @@ public class ConcurrentSkipListMap
if (q.unlink(r))
continue;
else
- break;
+ break;
}
if (c > 0) {
q = r;
@@ -1073,17 +1079,17 @@ public class ConcurrentSkipListMap
findNode(key); // cleans up
return;
}
- if (!q.link(r, t))
+ if (!q.link(r, t))
break; // restart
if (--insertionLevel == 0) {
// need final deletion check before return
- if (t.indexesDeletedNode())
- findNode(key);
+ if (t.indexesDeletedNode())
+ findNode(key);
return;
}
}
- if (j > insertionLevel && j <= indexLevel)
+ if (j > insertionLevel && j <= indexLevel)
t = t.down;
q = q.down;
--j;
@@ -1098,14 +1104,14 @@ public class ConcurrentSkipListMap
* deletion marker, unlinks predecessor, removes associated index
* nodes, and possibly reduces head index level.
*
- * Index node are cleared out simply by calling findPredecessor.
+ * 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.
+ * retention, so must call to be sure.
*
* @param okey the key
* @param value if non-null, the value that must be
@@ -1114,11 +1120,11 @@ public class ConcurrentSkipListMap
*/
private V doRemove(Object okey, Object value) {
Comparable key = comparable(okey);
- for (;;) {
+ for (;;) {
Node b = findPredecessor(key);
Node n = b.next;
for (;;) {
- if (n == null)
+ if (n == null)
return null;
Node f = n.next;
if (n != b.next) // inconsistent read
@@ -1138,15 +1144,15 @@ public class ConcurrentSkipListMap
n = f;
continue;
}
- if (value != null && !value.equals(v))
- return null;
- if (!n.casValue(v, null))
+ if (value != null && !value.equals(v))
+ return null;
+ if (!n.casValue(v, null))
break;
- if (!n.appendMarker(f) || !b.casNext(n, f))
+ if (!n.appendMarker(f) || !b.casNext(n, f))
findNode(key); // Retry via findNode
else {
findPredecessor(key); // Clean index
- if (head.right == null)
+ if (head.right == null)
tryReduceLevel();
}
return (V)v;
@@ -1179,16 +1185,22 @@ public class ConcurrentSkipListMap
HeadIndex d;
HeadIndex e;
if (h.level > 3 &&
- (d = (HeadIndex)h.down) != null &&
- (e = (HeadIndex)d.down) != null &&
- e.right == null &&
- d.right == null &&
+ (d = (HeadIndex)h.down) != null &&
+ (e = (HeadIndex)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
}
+ /**
+ * Version of remove with boolean return. Needed by view classes.
+ */
+ boolean removep(Object key) {
+ return doRemove(key, null) != null;
+ }
/* ---------------- Finding and removing first element -------------- */
@@ -1202,23 +1214,23 @@ public class ConcurrentSkipListMap
Node n = b.next;
if (n == null)
return null;
- if (n.value != null)
+ if (n.value != null)
return n;
n.helpDelete(b, n.next);
}
}
/**
- * Remove first entry; return either its key or a snapshot.
+ * Removes first entry; return either its key or a snapshot.
* @param keyOnly if true return key, else return SnapshotEntry
* (This is a little ugly, but avoids code duplication.)
* @return null if empty, first key if keyOnly true, else key,value entry
*/
Object doRemoveFirst(boolean keyOnly) {
- for (;;) {
+ for (;;) {
Node b = head.node;
Node n = b.next;
- if (n == null)
+ if (n == null)
return null;
Node f = n.next;
if (n != b.next)
@@ -1234,13 +1246,13 @@ public class ConcurrentSkipListMap
findFirst(); // retry
clearIndexToFirst();
K key = n.key;
- return (keyOnly)? key : new SnapshotEntry(key, (V)v);
+ return keyOnly ? key : new SnapshotEntry(key, (V)v);
}
}
/**
- * Clear out index nodes associated with deleted first entry.
- * Needed by doRemoveFirst
+ * Clears out index nodes associated with deleted first entry.
+ * Needed by doRemoveFirst.
*/
private void clearIndexToFirst() {
for (;;) {
@@ -1248,9 +1260,9 @@ public class ConcurrentSkipListMap
for (;;) {
Index r = q.right;
if (r != null && r.indexesDeletedNode() && !q.unlink(r))
- break;
+ break;
if ((q = q.down) == null) {
- if (head.right == null)
+ if (head.right == null)
tryReduceLevel();
return;
}
@@ -1258,6 +1270,13 @@ public class ConcurrentSkipListMap
}
}
+ /**
+ * Removes first entry; return key or null if empty.
+ */
+ K pollFirstKey() {
+ return (K)doRemoveFirst(true);
+ }
+
/* ---------------- Finding and removing last element -------------- */
/**
@@ -1277,7 +1296,7 @@ public class ConcurrentSkipListMap
if (r.indexesDeletedNode()) {
q.unlink(r);
q = head; // restart
- }
+ }
else
q = r;
} else if ((d = q.down) != null) {
@@ -1286,8 +1305,8 @@ public class ConcurrentSkipListMap
Node b = q.node;
Node n = b.next;
for (;;) {
- if (n == null)
- return (b.isBaseHeader())? null : b;
+ if (n == null)
+ return b.isBaseHeader() ? null : b;
Node f = n.next; // inconsistent read
if (n != b.next)
break;
@@ -1313,13 +1332,13 @@ public class ConcurrentSkipListMap
* @return null if empty, last key if keyOnly true, else key,value entry
*/
Object doRemoveLast(boolean keyOnly) {
- for (;;) {
+ for (;;) {
Node b = findPredecessorOfLast();
Node n = b.next;
if (n == null) {
if (b.isBaseHeader()) // empty
return null;
- else
+ else
continue; // all b's successors are deleted; retry
}
for (;;) {
@@ -1338,18 +1357,18 @@ public class ConcurrentSkipListMap
n = f;
continue;
}
- if (!n.casValue(v, null))
+ if (!n.casValue(v, null))
break;
K key = n.key;
Comparable ck = comparable(key);
- if (!n.appendMarker(f) || !b.casNext(n, f))
+ if (!n.appendMarker(f) || !b.casNext(n, f))
findNode(ck); // Retry via findNode
else {
findPredecessor(ck); // Clean index
- if (head.right == null)
+ if (head.right == null)
tryReduceLevel();
}
- return (keyOnly)? key : new SnapshotEntry(key, (V)v);
+ return keyOnly ? key : new SnapshotEntry(key, (V)v);
}
}
}
@@ -1359,7 +1378,7 @@ public class ConcurrentSkipListMap
* last valid node. Needed by doRemoveLast. 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.
+ * @return likely predecessor of last node
*/
private Node findPredecessorOfLast() {
for (;;) {
@@ -1377,21 +1396,28 @@ public class ConcurrentSkipListMap
continue;
}
}
- if ((d = q.down) != null)
+ if ((d = q.down) != null)
q = d;
- else
+ else
return q.node;
}
}
}
-
+
+ /**
+ * Removes last entry; return key or null if empty.
+ */
+ K pollLastKey() {
+ return (K)doRemoveLast(true);
+ }
+
/* ---------------- 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;
+ private static final int GT = 0; // Actually checked as !LT
/**
* Utility for ceiling, floor, lower, higher methods.
@@ -1405,8 +1431,8 @@ public class ConcurrentSkipListMap
Node b = findPredecessor(key);
Node n = b.next;
for (;;) {
- if (n == null)
- return ((rel & LT) == 0 || b.isBaseHeader())? null : b;
+ if (n == null)
+ return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b;
Node f = n.next;
if (n != b.next) // inconsistent read
break;
@@ -1422,7 +1448,7 @@ public class ConcurrentSkipListMap
(c < 0 && (rel & LT) == 0))
return n;
if ( c <= 0 && (rel & LT) != 0)
- return (b.isBaseHeader())? null : b;
+ return b.isBaseHeader() ? null : b;
b = n;
n = f;
}
@@ -1430,7 +1456,7 @@ public class ConcurrentSkipListMap
}
/**
- * Return SnapshotEntry for results of findNear.
+ * Returns SnapshotEntry for results of findNear.
* @param kkey the key
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return Entry fitting relation, or null if no such
@@ -1446,11 +1472,98 @@ public class ConcurrentSkipListMap
}
}
+ /**
+ * Returns ceiling, or first node if key is {@code null}.
+ */
+ Node findCeiling(K key) {
+ return (key == null) ? findFirst() : findNear(key, GT|EQ);
+ }
+
+ /**
+ * Returns lower node, or last node if key is {@code null}.
+ */
+ Node findLower(K key) {
+ return (key == null) ? findLast() : findNear(key, LT);
+ }
+
+ /**
+ * Returns SnapshotEntry or key for results of findNear ofter screening
+ * to ensure result is in given range. Needed by submaps.
+ * @param kkey the key
+ * @param rel the relation -- OR'ed combination of EQ, LT, GT
+ * @param least minimum allowed key value
+ * @param fence key greater than maximum allowed key value
+ * @param keyOnly if true return key, else return SnapshotEntry
+ * @return Key or Entry fitting relation, or {@code null} if no such
+ */
+ Object getNear(K kkey, int rel, K least, K fence, boolean keyOnly) {
+ K key = kkey;
+ // Don't return keys less than least
+ if ((rel & LT) == 0) {
+ if (compare(key, least) < 0) {
+ key = least;
+ rel = rel | EQ;
+ }
+ }
+
+ for (;;) {
+ Node n = findNear(key, rel);
+ if (n == null || !inHalfOpenRange(n.key, least, fence))
+ return null;
+ K k = n.key;
+ V v = n.getValidValue();
+ if (v != null)
+ return keyOnly ? k : new SnapshotEntry(k, v);
+ }
+ }
+
+ /**
+ * Finds and removes least element of subrange.
+ * @param least minimum allowed key value
+ * @param fence key greater than maximum allowed key value
+ * @param keyOnly if true return key, else return SnapshotEntry
+ * @return least Key or Entry, or {@code null} if no such
+ */
+ Object removeFirstEntryOfSubrange(K least, K fence, boolean keyOnly) {
+ for (;;) {
+ Node n = findCeiling(least);
+ if (n == null)
+ return null;
+ K k = n.key;
+ if (fence != null && compare(k, fence) >= 0)
+ return null;
+ V v = doRemove(k, null);
+ if (v != null)
+ return keyOnly ? k : new SnapshotEntry(k, v);
+ }
+ }
+
+ /**
+ * Finds and removes greatest element of subrange.
+ * @param least minimum allowed key value
+ * @param fence key greater than maximum allowed key value
+ * @param keyOnly if true return key, else return SnapshotEntry
+ * @return least Key or Entry, or {@code null} if no such
+ */
+ Object removeLastEntryOfSubrange(K least, K fence, boolean keyOnly) {
+ for (;;) {
+ Node n = findLower(fence);
+ if (n == null)
+ return null;
+ K k = n.key;
+ if (least != null && compare(k, least) < 0)
+ return null;
+ V v = doRemove(k, null);
+ if (v != null)
+ return keyOnly ? k : new SnapshotEntry(k, v);
+ }
+ }
+
/* ---------------- Constructors -------------- */
/**
* Constructs a new empty map, sorted according to the keys' natural
- * order.
+ * order.
*/
public ConcurrentSkipListMap() {
this.comparator = null;
@@ -1461,7 +1574,7 @@ public class ConcurrentSkipListMap
* Constructs a new empty map, sorted according to the given comparator.
*
* @param c the comparator that will be used to sort this map. A
- * null value indicates that the keys' natural
+ * {@code null} value indicates that the keys' natural
* ordering should be used.
*/
public ConcurrentSkipListMap(Comparator super K> c) {
@@ -1471,12 +1584,12 @@ public class ConcurrentSkipListMap
/**
* Constructs a new map containing the same mappings as the given map,
- * sorted according to the keys' natural order.
+ * sorted according to the keys' natural order.
*
- * @param m the map whose mappings are to be placed in this map.
+ * @param m the map whose mappings are to be placed in this map
* @throws ClassCastException if the keys in m are not Comparable, or
- * are not mutually comparable.
- * @throws NullPointerException if the specified map is null.
+ * are not mutually comparable
+ * @throws NullPointerException if the specified map is {@code null}
*/
public ConcurrentSkipListMap(Map extends K, ? extends V> m) {
this.comparator = null;
@@ -1486,11 +1599,11 @@ public class ConcurrentSkipListMap
/**
* Constructs a new map containing the same mappings as the given
- * SortedMap, sorted according to the same ordering.
+ * {@code SortedMap}, sorted according to the same ordering.
* @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.
+ * map, and whose comparator is to be used to sort this map
* @throws NullPointerException if the specified sorted map is
- * null.
+ * {@code null}
*/
public ConcurrentSkipListMap(SortedMap m) {
this.comparator = m.comparator();
@@ -1499,10 +1612,10 @@ public class ConcurrentSkipListMap
}
/**
- * Returns a shallow copy of this Map instance. (The keys and
+ * Returns a shallow copy of this {@code Map} instance. (The keys and
* values themselves are not cloned.)
*
- * @return a shallow copy of this Map.
+ * @return a shallow copy of this Map
*/
public Object clone() {
ConcurrentSkipListMap clone = null;
@@ -1534,7 +1647,7 @@ public class ConcurrentSkipListMap
ArrayList> preds = new ArrayList>();
// initialize
- for (int i = 0; i <= h.level; ++i)
+ for (int i = 0; i <= h.level; ++i)
preds.add(null);
Index q = h;
for (int i = h.level; i > 0; --i) {
@@ -1542,7 +1655,7 @@ public class ConcurrentSkipListMap
q = q.down;
}
- Iterator extends Map.Entry extends K, ? extends V>> it =
+ Iterator extends Map.Entry extends K, ? extends V>> it =
map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry extends K, ? extends V> e = it.next();
@@ -1559,7 +1672,7 @@ public class ConcurrentSkipListMap
Index idx = null;
for (int i = 1; i <= j; ++i) {
idx = new Index(z, idx, null);
- if (i > h.level)
+ if (i > h.level)
h = new HeadIndex(h.node, h, idx, i);
if (i < preds.size()) {
@@ -1576,11 +1689,11 @@ public class ConcurrentSkipListMap
/* ---------------- Serialization -------------- */
/**
- * Save the state of the Map instance to a stream.
+ * Saves the state of the {@code Map} instance to a stream.
*
* @serialData The key (Object) and value (Object) for each
- * key-value mapping represented by the Map, followed by
- * null. The key-value mappings are emitted in key-order
+ * key-value mapping represented by the Map, followed by
+ * {@code null}. The key-value mappings are emitted in key-order
* (as determined by the Comparator, or by the keys' natural
* ordering if no Comparator).
*/
@@ -1601,7 +1714,7 @@ public class ConcurrentSkipListMap
}
/**
- * Reconstitute the Map instance from a stream.
+ * Reconstitutes the {@code Map} instance from a stream.
*/
private void readObject(final java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1610,7 +1723,7 @@ public class ConcurrentSkipListMap
// 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
@@ -1621,7 +1734,7 @@ public class ConcurrentSkipListMap
HeadIndex h = head;
Node basepred = h.node;
ArrayList> preds = new ArrayList>();
- for (int i = 0; i <= h.level; ++i)
+ for (int i = 0; i <= h.level; ++i)
preds.add(null);
Index q = h;
for (int i = h.level; i > 0; --i) {
@@ -1634,7 +1747,7 @@ public class ConcurrentSkipListMap
if (k == null)
break;
Object v = s.readObject();
- if (v == null)
+ if (v == null)
throw new NullPointerException();
K key = (K) k;
V val = (V) v;
@@ -1647,7 +1760,7 @@ public class ConcurrentSkipListMap
Index idx = null;
for (int i = 1; i <= j; ++i) {
idx = new Index(z, idx, null);
- if (i > h.level)
+ if (i > h.level)
h = new HeadIndex(h.node, h, idx, i);
if (i < preds.size()) {
@@ -1664,14 +1777,14 @@ public class ConcurrentSkipListMap
/* ------ Map API methods ------ */
/**
- * Returns true if this map contains a mapping for the specified
+ * Returns {@code true} if this map contains a mapping for the specified
* key.
- * @param key key whose presence in this map is to be tested.
- * @return true if this map contains a mapping for the
- * specified key.
+ * @param key key whose presence in this map is to be tested
+ * @return {@code true} if this map contains a mapping for the
+ * specified key
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key is null.
+ * currently in the map
+ * @throws NullPointerException if the key is {@code null}
*/
public boolean containsKey(Object key) {
return doGet(key) != null;
@@ -1679,14 +1792,14 @@ public class ConcurrentSkipListMap
/**
* Returns the value to which this map maps the specified key. Returns
- * null if the map contains no mapping for this key.
+ * {@code null} if the map contains no mapping for this key.
*
- * @param key key whose associated value is to be returned.
+ * @param key key whose associated value is to be returned
* @return the value to which this map maps the specified key, or
- * null if the map contains no mapping for the key.
+ * {@code null} if the map contains no mapping for the key
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key is null.
+ * currently in the map
+ * @throws NullPointerException if the key is {@code null}
*/
public V get(Object key) {
return doGet(key);
@@ -1697,17 +1810,17 @@ public class ConcurrentSkipListMap
* If the map previously contained a mapping for this 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.
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
*
- * @return previous value associated with specified key, or null
- * if there was no mapping for key.
+ * @return previous value associated with specified key, or {@code null}
+ * if there was no mapping for key
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key or value are null.
+ * currently in the map
+ * @throws NullPointerException if the key or value are {@code null}
*/
public V put(K key, V value) {
- if (value == null)
+ if (value == null)
throw new NullPointerException();
return doPut(key, value, false);
}
@@ -1716,29 +1829,29 @@ public class ConcurrentSkipListMap
* Removes the mapping for this key from this Map if present.
*
* @param key key for which mapping should be removed
- * @return previous value associated with specified key, or null
- * if there was no mapping for key.
+ * @return previous value associated with specified key, or {@code null}
+ * if there was no mapping for key
*
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key is null.
+ * currently in the map
+ * @throws NullPointerException if the key is {@code null}
*/
public V remove(Object key) {
return doRemove(key, null);
}
/**
- * Returns true if this map maps one or more keys to the
+ * Returns {@code true} 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 true if a mapping to value exists;
- * false otherwise.
- * @throws NullPointerException if the value is null.
- */
+ * @param value value whose presence in this Map is to be tested
+ * @return {@code true} if a mapping to {@code value} exists;
+ * {@code false} otherwise
+ * @throws NullPointerException if the value is {@code null}
+ */
public boolean containsValue(Object value) {
- if (value == null)
+ if (value == null)
throw new NullPointerException();
for (Node n = findFirst(); n != null; n = n.next) {
V v = n.getValidValue();
@@ -1750,8 +1863,8 @@ public class ConcurrentSkipListMap
/**
* Returns the number of elements in this map. If this map
- * contains more than Integer.MAX_VALUE elements, it
- * returns Integer.MAX_VALUE.
+ * contains more than {@code Integer.MAX_VALUE} elements, it
+ * returns {@code Integer.MAX_VALUE}.
*
* Beware that, unlike in most collections, this method is
* NOT a constant-time operation. Because of the
@@ -1762,7 +1875,7 @@ public class ConcurrentSkipListMap
* will be inaccurate. Thus, this method is typically not very
* useful in concurrent applications.
*
- * @return the number of elements in this map.
+ * @return the number of elements in this map
*/
public int size() {
long count = 0;
@@ -1770,12 +1883,12 @@ public class ConcurrentSkipListMap
if (n.getValidValue() != null)
++count;
}
- return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count;
+ return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)count;
}
/**
- * Returns true if this map contains no key-value mappings.
- * @return true if this map contains no key-value mappings.
+ * Returns {@code true} if this map contains no key-value mappings.
+ * @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty() {
return findFirst() == null;
@@ -1792,17 +1905,17 @@ public class ConcurrentSkipListMap
* 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 Iterator.remove,
- * Set.remove, removeAll, retainAll, and
- * clear operations. It does not support the add or
- * addAll operations.
- * The view's iterator is a "weakly consistent" iterator that
+ * corresponding mapping from this 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.
+ * The view's {@code iterator} is a "weakly consistent" iterator that
* will never throw {@link java.util.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.
*
- * @return a set view of the keys contained in this map.
+ * @return a set view of the keys contained in this map
*/
public Set keySet() {
/*
@@ -1819,22 +1932,53 @@ public class ConcurrentSkipListMap
}
/**
+ * Returns a set view of the keys contained in this map in
+ * descending 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 this 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. The view's
+ * {@code iterator} is a "weakly consistent" iterator that will
+ * never throw {@link java.util.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.
+ *
+ * @return a set view of the keys contained in this map
+ */
+ public Set descendingKeySet() {
+ /*
+ * Note: Lazy intialization works here and for other 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.
+ */
+ DescendingKeySet ks = descendingKeySet;
+ return (ks != null) ? ks : (descendingKeySet = new DescendingKeySet());
+ }
+
+ /**
* 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 Iterator.remove,
- * Collection.remove, removeAll,
- * retainAll, and clear operations. It does not
- * support the add or addAll operations. The
- * view's iterator is a "weakly consistent" iterator that
+ * mapping from this map, via the {@code Iterator.remove},
+ * {@code Collection.remove}, {@code removeAll},
+ * {@code retainAll}, and {@code clear} operations. It does not
+ * support the {@code add} or {@code addAll} operations. The
+ * view's {@code iterator} is a "weakly consistent" iterator that
* will never throw {@link
* java.util.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.
*
- * @return a collection view of the values contained in this map.
+ * @return a collection view of the values contained in this map
*/
public Collection values() {
Values vs = values;
@@ -1844,58 +1988,85 @@ public class ConcurrentSkipListMap
/**
* Returns a collection view of the mappings contained in this
* map. Each element in the returned collection is a
- * Map.Entry. The collection is backed by the map, so
+ * {@code Map.Entry}. 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
- * Iterator.remove, Collection.remove,
- * removeAll, retainAll, and clear
- * operations. It does not support the add or
- * addAll operations. The view's iterator is a
+ * {@code Iterator.remove}, {@code Collection.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear}
+ * operations. It does not support the {@code add} or
+ * {@code addAll} operations. The view's {@code iterator} is a
* "weakly consistent" iterator that will never throw {@link
* java.util.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. The
- * Map.Entry elements returned by
- * iterator.next() do not support the
- * setValue operation.
+ * {@code Map.Entry} elements returned by
+ * {@code iterator.next()} do not support the
+ * {@code setValue} operation.
*
- * @return a collection view of the mappings contained in this map.
+ * @return a collection view of the mappings contained in this map
*/
public Set> entrySet() {
EntrySet es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
+ /**
+ * Returns a collection view of the mappings contained in this
+ * map, in descending order. Each element in the returned
+ * collection is a {@code Map.Entry}. 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 {@code Iterator.remove}, {@code Collection.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear}
+ * operations. It does not support the {@code add} or
+ * {@code addAll} operations. The view's {@code iterator} is a
+ * "weakly consistent" iterator that will never throw {@link
+ * java.util.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. The
+ * {@code Map.Entry} elements returned by
+ * {@code iterator.next()} do not support the
+ * {@code setValue} operation.
+ *
+ * @return a collection view of the mappings contained in this map
+ */
+ public Set> descendingEntrySet() {
+ DescendingEntrySet es = descendingEntrySet;
+ return (es != null) ? es : (descendingEntrySet = new DescendingEntrySet());
+ }
+
/* ---------------- AbstractMap Overrides -------------- */
/**
* Compares the specified object with this map for equality.
- * Returns true if the given object is also a map and the
+ * Returns {@code true} if the given object is also a map and the
* two maps represent the same mappings. More formally, two maps
- * t1 and t2 represent the same mappings if
- * t1.keySet().equals(t2.keySet()) and for every key
- * k in t1.keySet(), (t1.get(k)==null ?
- * t2.get(k)==null : t1.get(k).equals(t2.get(k))) . This
+ * {@code t1} and {@code t2} represent the same mappings if
+ * {@code t1.keySet().equals(t2.keySet())} and for every key
+ * {@code k} in {@code t1.keySet()}, {@code (t1.get(k)==null ?
+ * t2.get(k)==null : t1.get(k).equals(t2.get(k))) }. 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 true if the specified object is equal to this map.
+ * @param o object to be compared for equality with this map
+ * @return {@code true} 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 t = (Map) o;
+ if (o == this)
+ return true;
+ if (!(o instanceof Map))
+ return false;
+ Map t = (Map) o;
try {
- return (containsAllMappings(this, t) &&
+ return (containsAllMappings(this, t) &&
containsAllMappings(t, this));
- } catch(ClassCastException unused) {
+ } catch (ClassCastException unused) {
return false;
- } catch(NullPointerException unused) {
+ } catch (NullPointerException unused) {
return false;
}
}
@@ -1909,7 +2080,7 @@ public class ConcurrentSkipListMap
Entry e = it.next();
Object k = e.getKey();
Object v = e.getValue();
- if (k == null || v == null || !v.equals(a.get(k)))
+ if (k == null || v == null || !v.equals(a.get(k)))
return false;
}
return true;
@@ -1922,71 +2093,71 @@ public class ConcurrentSkipListMap
* with a value, associate it with the given value.
* This is equivalent to
*
- * if (!map.containsKey(key))
- * return map.put(key, value);
+ * if (!map.containsKey(key))
+ * return map.put(key, value);
* else
- * return map.get(key);
+ * return map.get(key);
*
* except that the action is performed atomically.
- * @param key key with which the specified value is to be associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or null
- * if there was no mapping for key.
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return previous value associated with specified key, or {@code null}
+ * if there was no mapping for key
*
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key or value are null.
+ * currently in the map
+ * @throws NullPointerException if the key or value are {@code null}
*/
public V putIfAbsent(K key, V value) {
- if (value == null)
+ if (value == null)
throw new NullPointerException();
return doPut(key, value, true);
}
/**
- * Remove entry for key only if currently mapped to given value.
+ * Removes entry for key only if currently mapped to given value.
* Acts as
- *
+ *
* if ((map.containsKey(key) && map.get(key).equals(value)) {
* map.remove(key);
* return true;
* } else return false;
*
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value associated with the specified key.
+ * @param key key with which the specified value is associated
+ * @param value value associated with the specified key
* @return true if the value was removed, false otherwise
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key or value are null.
+ * currently in the map
+ * @throws NullPointerException if the key or value are {@code null}
*/
public boolean remove(Object key, Object value) {
- if (value == null)
+ if (value == null)
throw new NullPointerException();
return doRemove(key, value) != null;
}
/**
- * Replace entry for key only if currently mapped to given value.
+ * Replaces entry for key only if currently mapped to given value.
* Acts as
- *
+ *
* if ((map.containsKey(key) && map.get(key).equals(oldValue)) {
* map.put(key, newValue);
* return true;
* } else return false;
*
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param oldValue value expected to be associated with the specified key.
- * @param newValue value to be associated with the specified key.
+ * @param key key with which the specified value is associated
+ * @param oldValue value expected to be associated with the specified key
+ * @param newValue value to be associated with the specified key
* @return true if the value was replaced
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
+ * currently in the map
* @throws NullPointerException if key, oldValue or newValue are
- * null.
+ * {@code null}
*/
public boolean replace(K key, V oldValue, V newValue) {
- if (oldValue == null || newValue == null)
+ if (oldValue == null || newValue == null)
throw new NullPointerException();
Comparable k = comparable(key);
for (;;) {
@@ -2004,24 +2175,24 @@ public class ConcurrentSkipListMap
}
/**
- * Replace entry for key only if currently mapped to some value.
+ * Replaces entry for key only if currently mapped to some value.
* Acts as
- *
+ *
* if ((map.containsKey(key)) {
* return map.put(key, value);
* } else return null;
*
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or null
- * if there was no mapping for key.
+ * @param key key with which the specified value is associated
+ * @param value value to be associated with the specified key
+ * @return previous value associated with specified key, or {@code null}
+ * if there was no mapping for key
* @throws ClassCastException if the key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if the key or value are null.
+ * currently in the map
+ * @throws NullPointerException if the key or value are {@code null}
*/
public V replace(K key, V value) {
- if (value == null)
+ if (value == null)
throw new NullPointerException();
Comparable k = comparable(key);
for (;;) {
@@ -2037,11 +2208,11 @@ public class ConcurrentSkipListMap
/* ------ SortedMap API methods ------ */
/**
- * Returns the comparator used to order this map, or null
+ * Returns the comparator used to order this map, or {@code null}
* if this map uses its keys' natural order.
*
* @return the comparator associated with this map, or
- * null if it uses its keys' natural sort method.
+ * {@code null} if it uses its keys' natural sort method
*/
public Comparator super K> comparator() {
return comparator;
@@ -2050,10 +2221,10 @@ public class ConcurrentSkipListMap
/**
* Returns the first (lowest) key currently in this map.
*
- * @return the first (lowest) key currently in this map.
- * @throws NoSuchElementException Map is empty.
+ * @return the first (lowest) key currently in this map
+ * @throws NoSuchElementException Map is empty
*/
- public K firstKey() {
+ public K firstKey() {
Node n = findFirst();
if (n == null)
throw new NoSuchElementException();
@@ -2063,8 +2234,8 @@ public class ConcurrentSkipListMap
/**
* Returns the last (highest) key currently in this map.
*
- * @return the last (highest) key currently in this map.
- * @throws NoSuchElementException Map is empty.
+ * @return the last (highest) key currently in this map
+ * @throws NoSuchElementException Map is empty
*/
public K lastKey() {
Node n = findLast();
@@ -2075,24 +2246,24 @@ public class ConcurrentSkipListMap
/**
* Returns a view of the portion of this map whose keys range from
- * fromKey, inclusive, to toKey, exclusive. (If
- * fromKey and toKey are equal, the returned sorted map
+ * {@code fromKey}, inclusive, to {@code toKey}, exclusive. (If
+ * {@code fromKey} and {@code toKey} are equal, the returned sorted map
* is empty.) The returned sorted map is backed by this map, so changes
* in the returned sorted map are reflected in this map, and vice-versa.
-
- * @param fromKey low endpoint (inclusive) of the subMap.
- * @param toKey high endpoint (exclusive) of the subMap.
+ *
+ * @param fromKey low endpoint (inclusive) of the subMap
+ * @param toKey high endpoint (exclusive) of the subMap
*
* @return a view of the portion of this map whose keys range from
- * fromKey, inclusive, to toKey, exclusive.
+ * {@code fromKey}, inclusive, to {@code toKey}, exclusive
*
- * @throws ClassCastException if fromKey and toKey
+ * @throws ClassCastException if {@code fromKey} and {@code toKey}
* cannot be compared to one another using this map's comparator
- * (or, if the map has no comparator, using natural ordering).
- * @throws IllegalArgumentException if fromKey is greater than
- * toKey.
- * @throws NullPointerException if fromKey or toKey is
- * null.
+ * (or, if the map has no comparator, using natural ordering)
+ * @throws IllegalArgumentException if {@code fromKey} is greater than
+ * {@code toKey}
+ * @throws NullPointerException if {@code fromKey} or {@code toKey} is
+ * {@code null}
*/
public ConcurrentNavigableMap subMap(K fromKey, K toKey) {
if (fromKey == null || toKey == null)
@@ -2102,17 +2273,17 @@ public class ConcurrentSkipListMap
/**
* Returns a view of the portion of this map whose keys are
- * strictly less than toKey. The returned sorted map is
+ * strictly less than {@code toKey}. The returned sorted map is
* backed by this map, so changes in the returned sorted map are
* reflected in this map, and vice-versa.
- * @param toKey high endpoint (exclusive) of the headMap.
+ * @param toKey high endpoint (exclusive) of the headMap
* @return a view of the portion of this map whose keys are
- * strictly less than toKey.
+ * strictly less than {@code toKey}
*
- * @throws ClassCastException if toKey is not compatible
+ * @throws ClassCastException if {@code toKey} is not compatible
* with this map's comparator (or, if the map has no comparator,
- * if toKey does not implement Comparable).
- * @throws NullPointerException if toKey is null.
+ * if {@code toKey} does not implement {@code Comparable})
+ * @throws NullPointerException if {@code toKey} is {@code null}
*/
public ConcurrentNavigableMap headMap(K toKey) {
if (toKey == null)
@@ -2122,19 +2293,19 @@ public class ConcurrentSkipListMap
/**
* Returns a view of the portion of this map whose keys are
- * greater than or equal to fromKey. The returned sorted
+ * greater than or equal to {@code fromKey}. The returned sorted
* map is backed by this map, so changes in the returned sorted
* map are reflected in this map, and vice-versa.
- * @param fromKey low endpoint (inclusive) of the tailMap.
+ * @param fromKey low endpoint (inclusive) of the tailMap
* @return a view of the portion of this map whose keys are
- * greater than or equal to fromKey.
- * @throws ClassCastException if fromKey is not
+ * greater than or equal to {@code fromKey}
+ * @throws ClassCastException if {@code fromKey} is not
* compatible with this map's comparator (or, if the map has no
- * comparator, if fromKey does not implement
- * Comparable).
- * @throws NullPointerException if fromKey is null.
+ * comparator, if {@code fromKey} does not implement
+ * {@code Comparable})
+ * @throws NullPointerException if {@code fromKey} is {@code null}
*/
- public ConcurrentNavigableMap tailMap(K fromKey) {
+ public ConcurrentNavigableMap tailMap(K fromKey) {
if (fromKey == null)
throw new NullPointerException();
return new ConcurrentSkipListSubMap(this, fromKey, null);
@@ -2144,85 +2315,150 @@ public class ConcurrentSkipListMap
/**
* Returns a key-value mapping associated with the least key
- * greater than or equal to the given key, or null if
+ * greater than or equal to the given key, or {@code null} if
* there is no such entry. The returned entry does not
- * support the Entry.setValue method.
- *
- * @param key the key.
+ * support the {@code Entry.setValue} method.
+ *
+ * @param key the key
* @return an Entry associated with ceiling of given key, or
- * null if there is no such Entry.
+ * {@code null} if there is no such Entry
* @throws ClassCastException if key cannot be compared with the
- * keys currently in the map.
- * @throws NullPointerException if key is null.
+ * keys currently in the map
+ * @throws NullPointerException if key is {@code null}
*/
public Map.Entry ceilingEntry(K key) {
return getNear(key, GT|EQ);
}
/**
+ * Returns least key greater than or equal to the given key, or
+ * {@code null} if there is no such key.
+ *
+ * @param key the key
+ * @return the ceiling key, or {@code null}
+ * if there is no such key
+ * @throws ClassCastException if key cannot be compared with the keys
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
+ */
+ public K ceilingKey(K key) {
+ Node n = findNear(key, GT|EQ);
+ return (n == null) ? null : n.key;
+ }
+
+ /**
* Returns a key-value mapping associated with the greatest
- * key strictly less than the given key, or null if there is no
+ * key strictly less than the given key, or {@code null} if there is no
* such entry. The returned entry does not support
- * the Entry.setValue method.
- *
- * @param key the key.
+ * the {@code Entry.setValue} method.
+ *
+ * @param key the key
* @return an Entry with greatest key less than the given
- * key, or null if there is no such Entry.
+ * key, or {@code null} if there is no such Entry
* @throws ClassCastException if key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if key is null.
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
*/
public Map.Entry lowerEntry(K key) {
return getNear(key, LT);
}
/**
+ * Returns the greatest key strictly less than the given key, or
+ * {@code null} if there is no such key.
+ *
+ * @param key the key
+ * @return the greatest key less than the given
+ * key, or {@code null} if there is no such key
+ * @throws ClassCastException if key cannot be compared with the keys
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
+ */
+ public K lowerKey(K key) {
+ Node 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 null if there
+ * less than or equal to the given key, or {@code null} if there
* is no such entry. The returned entry does not support
- * the Entry.setValue method.
- *
- * @param key the key.
- * @return an Entry associated with floor of given key, or null
- * if there is no such Entry.
+ * the {@code Entry.setValue} method.
+ *
+ * @param key the key
+ * @return an Entry associated with floor of given key, or {@code null}
+ * if there is no such Entry
* @throws ClassCastException if key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if key is null.
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
*/
public Map.Entry floorEntry(K key) {
return getNear(key, LT|EQ);
}
/**
+ * Returns the greatest key
+ * less than or equal to the given key, or {@code null} if there
+ * is no such key.
+ *
+ * @param key the key
+ * @return the floor of given key, or {@code null} if there is no
+ * such key
+ * @throws ClassCastException if key cannot be compared with the keys
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
+ */
+ public K floorKey(K key) {
+ Node n = findNear(key, LT|EQ);
+ return (n == null) ? null : n.key;
+ }
+
+ /**
* Returns a key-value mapping associated with the least key
- * strictly greater than the given key, or null if there
+ * strictly greater than the given key, or {@code null} if there
* is no such entry. The returned entry does not support
- * the Entry.setValue method.
- *
- * @param key the key.
+ * the {@code Entry.setValue} method.
+ *
+ * @param key the key
* @return an Entry with least key greater than the given key, or
- * null if there is no such Entry.
+ * {@code null} if there is no such Entry
* @throws ClassCastException if key cannot be compared with the keys
- * currently in the map.
- * @throws NullPointerException if key is null.
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
*/
public Map.Entry higherEntry(K key) {
return getNear(key, GT);
}
/**
+ * Returns the least key strictly greater than the given key, or
+ * {@code null} if there is no such key.
+ *
+ * @param key the key
+ * @return the least key greater than the given key, or
+ * {@code null} if there is no such key
+ * @throws ClassCastException if key cannot be compared with the keys
+ * currently in the map
+ * @throws NullPointerException if key is {@code null}
+ */
+ public K higherKey(K key) {
+ Node n = findNear(key, GT);
+ return (n == null) ? null : n.key;
+ }
+
+ /**
* Returns a key-value mapping associated with the least
- * key in this map, or null if the map is empty.
+ * key in this map, or {@code null} if the map is empty.
* The returned entry does not support
- * the Entry.setValue method.
- *
- * @return an Entry with least key, or null
- * if the map is empty.
+ * the {@code Entry.setValue} method.
+ *
+ * @return an Entry with least key, or {@code null}
+ * if the map is empty
*/
public Map.Entry firstEntry() {
for (;;) {
Node n = findFirst();
- if (n == null)
+ if (n == null)
return null;
SnapshotEntry e = n.createSnapshot();
if (e != null)
@@ -2232,17 +2468,17 @@ public class ConcurrentSkipListMap
/**
* Returns a key-value mapping associated with the greatest
- * key in this map, or null if the map is empty.
+ * key in this map, or {@code null} if the map is empty.
* The returned entry does not support
- * the Entry.setValue method.
- *
- * @return an Entry with greatest key, or null
- * if the map is empty.
+ * the {@code Entry.setValue} method.
+ *
+ * @return an Entry with greatest key, or {@code null}
+ * if the map is empty
*/
public Map.Entry lastEntry() {
for (;;) {
Node n = findLast();
- if (n == null)
+ if (n == null)
return null;
SnapshotEntry e = n.createSnapshot();
if (e != null)
@@ -2252,12 +2488,12 @@ public class ConcurrentSkipListMap
/**
* Removes and returns a key-value mapping associated with
- * the least key in this map, or null if the map is empty.
+ * the least key in this map, or {@code null} if the map is empty.
* The returned entry does not support
- * the Entry.setValue method.
- *
- * @return the removed first entry of this map, or null
- * if the map is empty.
+ * the {@code Entry.setValue} method.
+ *
+ * @return the removed first entry of this map, or {@code null}
+ * if the map is empty
*/
public Map.Entry pollFirstEntry() {
return (SnapshotEntry)doRemoveFirst(false);
@@ -2265,35 +2501,43 @@ public class ConcurrentSkipListMap
/**
* Removes and returns a key-value mapping associated with
- * the greatest key in this map, or null if the map is empty.
+ * the greatest key in this map, or {@code null} if the map is empty.
* The returned entry does not support
- * the Entry.setValue method.
- *
- * @return the removed last entry of this map, or null
- * if the map is empty.
+ * the {@code Entry.setValue} method.
+ *
+ * @return the removed last entry of this map, or {@code null}
+ * if the map is empty
*/
public Map.Entry pollLastEntry() {
return (SnapshotEntry)doRemoveLast(false);
}
+
/* ---------------- Iterators -------------- */
/**
- * Base of iterator classes.
- * (Six kinds: {key, value, entry} X {map, submap})
+ * Base of ten kinds of iterator classes:
+ * ascending: {map, submap} X {key, value, entry}
+ * descending: {map, submap} X {key, entry}
*/
- abstract class ConcurrentSkipListMapIterator {
+ abstract class Iter {
/** the last node returned by next() */
Node last;
/** the next node to return from next(); */
Node next;
- /** Cache of next value field to maintain weak consistency */
- Object nextValue;
+ /** Cache of next value field to maintain weak consistency */
+ Object nextValue;
+
+ Iter() {}
+
+ public final boolean hasNext() {
+ return next != null;
+ }
- /** Create normal iterator for entire range */
- ConcurrentSkipListMapIterator() {
+ /** initialize ascending iterator for entire range */
+ final void initAscending() {
for (;;) {
- next = findFirst();
+ next = findFirst();
if (next == null)
break;
nextValue = next.value;
@@ -2302,14 +2546,48 @@ public class ConcurrentSkipListMap
}
}
- /**
- * Create a submap iterator starting at given least key, or
- * first node if least is null, but not greater or equal to
- * fence, or end if fence is null.
+ /**
+ * initialize ascending iterator starting at given least key,
+ * or first node if least is {@code null}, but not greater or
+ * equal to fence, or end if fence is {@code null}.
*/
- ConcurrentSkipListMapIterator(K least, K fence) {
+ final void initAscending(K least, K fence) {
for (;;) {
- next = findCeiling(least);
+ next = findCeiling(least);
+ if (next == null)
+ break;
+ nextValue = next.value;
+ if (nextValue != null && nextValue != next) {
+ if (fence != null && compare(fence, next.key) <= 0) {
+ next = null;
+ nextValue = null;
+ }
+ break;
+ }
+ }
+ }
+ /** advance next to higher entry */
+ final void ascend() {
+ if ((last = next) == null)
+ throw new NoSuchElementException();
+ for (;;) {
+ next = next.next;
+ if (next == null)
+ break;
+ nextValue = next.value;
+ if (nextValue != null && nextValue != next)
+ break;
+ }
+ }
+
+ /**
+ * Version of ascend for submaps to stop at fence
+ */
+ final void ascend(K fence) {
+ if ((last = next) == null)
+ throw new NoSuchElementException();
+ for (;;) {
+ next = next.next;
if (next == null)
break;
nextValue = next.value;
@@ -2323,15 +2601,47 @@ public class ConcurrentSkipListMap
}
}
- public final boolean hasNext() {
- return next != null;
+ /** initialize descending iterator for entire range */
+ final void initDescending() {
+ for (;;) {
+ next = findLast();
+ if (next == null)
+ break;
+ nextValue = next.value;
+ if (nextValue != null && nextValue != next)
+ break;
+ }
}
- final void advance() {
+ /**
+ * initialize descending iterator starting at key less
+ * than or equal to given fence key, or
+ * last node if fence is {@code null}, but not less than
+ * least, or beginning if lest is {@code null}.
+ */
+ final void initDescending(K least, K fence) {
+ for (;;) {
+ next = findLower(fence);
+ if (next == null)
+ break;
+ nextValue = next.value;
+ if (nextValue != null && nextValue != next) {
+ if (least != null && compare(least, next.key) > 0) {
+ next = null;
+ nextValue = null;
+ }
+ break;
+ }
+ }
+ }
+
+ /** advance next to lower entry */
+ final void descend() {
if ((last = next) == null)
throw new NoSuchElementException();
+ K k = last.key;
for (;;) {
- next = next.next;
+ next = findNear(k, LT);
if (next == null)
break;
nextValue = next.value;
@@ -2341,18 +2651,19 @@ public class ConcurrentSkipListMap
}
/**
- * Version of advance for submaps to stop at fence
+ * Version of descend for submaps to stop at least
*/
- final void advance(K fence) {
+ final void descend(K least) {
if ((last = next) == null)
throw new NoSuchElementException();
+ K k = last.key;
for (;;) {
- next = next.next;
+ next = findNear(k, LT);
if (next == null)
break;
nextValue = next.value;
if (nextValue != null && nextValue != next) {
- if (fence != null && compare(fence, next.key) <= 0) {
+ if (least != null && compare(least, next.key) > 0) {
next = null;
nextValue = null;
}
@@ -2369,22 +2680,80 @@ public class ConcurrentSkipListMap
// unlink from here. Using remove is fast enough.
ConcurrentSkipListMap.this.remove(l.key);
}
+
}
- final class ValueIterator extends ConcurrentSkipListMapIterator
- implements Iterator {
- public V next() {
+ final class ValueIterator extends Iter implements Iterator {
+ ValueIterator() {
+ initAscending();
+ }
+ public V next() {
Object v = nextValue;
- advance();
+ ascend();
return (V)v;
}
}
- final class KeyIterator extends ConcurrentSkipListMapIterator
- implements Iterator {
- public K next() {
+ final class KeyIterator extends Iter implements Iterator