/* * 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/publicdomain/zero/1.0/ */ package jsr166x; import java.util.*; import java.util.concurrent.*; /** * A scalable concurrent {@link NavigableSet} implementation based on * a {@link ConcurrentSkipListMap}. This class maintains a set in * ascending order, sorted according to the natural order for * the element's class (see {@link Comparable}), or by the comparator * provided at creation time, depending on which constructor is * used.

* * This implementation provides expected average log(n) time * cost for the contains, add, and remove * operations and their variants. Insertion, removal, and access * operations safely execute concurrently by multiple * threads. Iterators are weakly consistent, returning elements * reflecting the state of the set at some point at or since the * creation of the iterator. They do not throw {@link * ConcurrentModificationException}, and may proceed concurrently with * other operations. * *

Beware that, unlike in most collections, the size * method is not a constant-time operation. Because of the * asynchronous nature of these sets, determining the current number * of elements requires a traversal of the elements. Additionally, the * bulk operations addAll, removeAll, * <retainAll, and tt>containsAll are not * guaranteed to be performed atomically. For example, an iterator * operating concurrently with an addAll operation might view * only some of the added elements. * *

This class and its iterators implement all of the * optional methods of the {@link Set} and {@link Iterator} * interfaces. Like most other concurrent collection implementations, * this class does not permit the use of null elements. * because null arguments and return values cannot be reliably * distinguished from the absence of elements. * * @author Doug Lea * @param the type of elements maintained by this set */ public class ConcurrentSkipListSet extends AbstractSet implements NavigableSet, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2479143111061671589L; /** * The underlying map. Uses Boolean.TRUE as value for each * element. Note that this class relies on default serialization, * which is a little wasteful in saving all of the useless value * fields of underlying map, but enables this field to be declared * final, which is necessary for thread safety. */ private final ConcurrentSkipListMap m; /** * Constructs a new, empty set, sorted according to the elements' natural * order. */ public ConcurrentSkipListSet() { m = new ConcurrentSkipListMap(); } /** * Constructs a new, empty set, sorted according to the specified * comparator. * * @param c the comparator that will be used to sort this set. A * null value indicates that the elements' natural * ordering should be used. */ public ConcurrentSkipListSet(Comparator c) { m = new ConcurrentSkipListMap(c); } /** * Constructs a new set containing the elements in the specified * collection, sorted according to the elements' natural order. * * @param c The elements that will comprise the new set. * * @throws ClassCastException if the elements in the specified * collection are not comparable, or are not mutually comparable. * @throws NullPointerException if the specified collection is * null. */ public ConcurrentSkipListSet(Collection c) { m = new ConcurrentSkipListMap(); addAll(c); } /** * Constructs a new set containing the same elements as the specified * sorted set, sorted according to the same ordering. * * @param s sorted set whose elements will comprise the new set. * @throws NullPointerException if the specified sorted set is * null. */ public ConcurrentSkipListSet(SortedSet s) { m = new ConcurrentSkipListMap(s.comparator()); addAll(s); } /** * Returns a shallow copy of this set. (The elements themselves * are not cloned.) * * @return a shallow copy of this set. */ public Object clone() { ConcurrentSkipListSet clone = null; try { clone = (ConcurrentSkipListSet) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } clone.m.initialize(); clone.addAll(this); return clone; } /* ---------------- Set operations -------------- */ /** * Returns the number of elements in this set. If this set * contains more than Integer.MAX_VALUE elements, it * returns Integer.MAX_VALUE. * *

Beware that, unlike in most collections, this method is * NOT a constant-time operation. Because of the * asynchronous nature of these sets, 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 set. */ public int size() { return m.size(); } /** * Returns true if this set contains no elements. * @return true if this set contains no elements. */ public boolean isEmpty() { return m.isEmpty(); } /** * Returns true if this set contains the specified element. * * @param o the object to be checked for containment in this set. * @return true if this set contains the specified element. * * @throws ClassCastException if the specified object cannot be compared * with the elements currently in the set. * @throws NullPointerException if o is null. */ public boolean contains(Object o) { return m.containsKey(o); } /** * Adds the specified element to this set if it is not already present. * * @param o element to be added to this set. * @return true if the set did not already contain the specified * element. * * @throws ClassCastException if the specified object cannot be compared * with the elements currently in the set. * @throws NullPointerException if o is null. */ public boolean add(E o) { return m.putIfAbsent(o, Boolean.TRUE) == null; } /** * Removes the specified element from this set if it is present. * * @param o object to be removed from this set, if present. * @return true if the set contained the specified element. * * @throws ClassCastException if the specified object cannot be compared * with the elements currently in the set. * @throws NullPointerException if o is null. */ public boolean remove(Object o) { return m.removep(o); } /** * Removes all of the elements from this set. */ public void clear() { m.clear(); } /** * Returns an iterator over the elements in this set. The elements * are returned in ascending order. * * @return an iterator over the elements in this set. */ public Iterator iterator() { return m.keyIterator(); } /** * Returns an iterator over the elements in this set. The elements * are returned in descending order. * * @return an iterator over the elements in this set. */ public Iterator descendingIterator() { return m.descendingKeyIterator(); } /* ---------------- AbstractSet Overrides -------------- */ /** * Compares the specified object with this set for equality. Returns * true if the specified object is also a set, the two sets * have the same size, and every member of the specified set is * contained in this set (or equivalently, every member of this set is * contained in the specified set). This definition ensures that the * equals method works properly across different implementations of the * set interface. * * @param o Object to be compared for equality with this set. * @return true if the specified Object is equal to this set. */ public boolean equals(Object o) { // Override AbstractSet version to avoid calling size() 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; } } /** * Removes from this set all of its elements that are contained in * the specified collection. If the specified collection is also * a set, this operation effectively modifies this set so that its * value is the asymmetric set difference of the two sets. * * @param c collection that defines which elements will be removed from * this set. * @return true if this set changed as a result of the call. * * @throws ClassCastException if the types of one or more elements in this * set are incompatible with the specified collection * @throws NullPointerException if the specified collection, or any * of its elements are null. */ public boolean removeAll(Collection c) { // Override AbstractSet version to avoid unnecessary call to size() boolean modified = false; for (Iterator i = c.iterator(); i.hasNext(); ) if (remove(i.next())) modified = true; return modified; } /* ---------------- Relational operations -------------- */ /** * Returns an element greater than or equal to the given element, or * null if there is no such element. * * @param o the value to match * @return an element greater than or equal to given element, or * null if there is no such element. * @throws ClassCastException if o cannot be compared with the elements * currently in the set. * @throws NullPointerException if o is null */ public E ceiling(E o) { return m.ceilingKey(o); } /** * Returns an element strictly less than the given element, or * null if there is no such element. * * @param o the value to match * @return the greatest element less than the given element, or * null if there is no such element. * @throws ClassCastException if o cannot be compared with the elements * currently in the set. * @throws NullPointerException if o is null. */ public E lower(E o) { return m.lowerKey(o); } /** * Returns an element less than or equal to the given element, or * null if there is no such element. * * @param o the value to match * @return the greatest element less than or equal to given * element, or null if there is no such element. * @throws ClassCastException if o cannot be compared with the elements * currently in the set. * @throws NullPointerException if o is null. */ public E floor(E o) { return m.floorKey(o); } /** * Returns an element strictly greater than the given element, or * null if there is no such element. * * @param o the value to match * @return the least element greater than the given element, or * null if there is no such element. * @throws ClassCastException if o cannot be compared with the elements * currently in the set. * @throws NullPointerException if o is null. */ public E higher(E o) { return m.higherKey(o); } /** * Retrieves and removes the first (lowest) element. * * @return the least element, or null if empty. */ public E pollFirst() { return m.pollFirstKey(); } /** * Retrieves and removes the last (highest) element. * * @return the last element, or null if empty. */ public E pollLast() { return m.pollLastKey(); } /* ---------------- SortedSet operations -------------- */ /** * Returns the comparator used to order this set, or null * if this set uses its elements natural ordering. * * @return the comparator used to order this set, or null * if this set uses its elements natural ordering. */ public Comparator comparator() { return m.comparator(); } /** * Returns the first (lowest) element currently in this set. * * @return the first (lowest) element currently in this set. * @throws NoSuchElementException sorted set is empty. */ public E first() { return m.firstKey(); } /** * Returns the last (highest) element currently in this set. * * @return the last (highest) element currently in this set. * @throws NoSuchElementException sorted set is empty. */ public E last() { return m.lastKey(); } /** * Returns a view of the portion of this set whose elements range from * fromElement, inclusive, to toElement, exclusive. (If * fromElement and toElement are equal, the returned * sorted set is empty.) The returned sorted set is backed by this set, * so changes in the returned sorted set are reflected in this set, and * vice-versa. * @param fromElement low endpoint (inclusive) of the subSet. * @param toElement high endpoint (exclusive) of the subSet. * @return a view of the portion of this set whose elements range from * fromElement, inclusive, to toElement, * exclusive. * @throws ClassCastException if fromElement and * toElement cannot be compared to one another using * this set's comparator (or, if the set has no comparator, * using natural ordering). * @throws IllegalArgumentException if fromElement is * greater than toElement. * @throws NullPointerException if fromElement or * toElement is null. */ public NavigableSet subSet(E fromElement, E toElement) { return new ConcurrentSkipListSubSet(m, fromElement, toElement); } /** * Returns a view of the portion of this set whose elements are strictly * less than toElement. The returned sorted set is backed by * this set, so changes in the returned sorted set are reflected in this * set, and vice-versa. * @param toElement high endpoint (exclusive) of the headSet. * @return a view of the portion of this set whose elements are strictly * less than toElement. * @throws ClassCastException if toElement is not compatible * with this set's comparator (or, if the set has no comparator, * if toElement does not implement Comparable). * @throws NullPointerException if toElement is null. */ public NavigableSet headSet(E toElement) { return new ConcurrentSkipListSubSet(m, null, toElement); } /** * Returns a view of the portion of this set whose elements are * greater than or equal to fromElement. The returned * sorted set is backed by this set, so changes in the returned * sorted set are reflected in this set, and vice-versa. * @param fromElement low endpoint (inclusive) of the tailSet. * @return a view of the portion of this set whose elements are * greater than or equal to fromElement. * @throws ClassCastException if fromElement is not * compatible with this set's comparator (or, if the set has no * comparator, if fromElement does not implement * Comparable). * @throws NullPointerException if fromElement is null. */ public NavigableSet tailSet(E fromElement) { return new ConcurrentSkipListSubSet(m, fromElement, null); } /** * Subsets returned by {@link ConcurrentSkipListSet} subset operations * represent a subrange of elements of their underlying * sets. Instances of this class support all methods of their * underlying sets, differing in that elements outside their range are * ignored, and attempts to add elements outside their ranges result * in {@link IllegalArgumentException}. Instances of this class are * constructed only using the subSet, headSet, and * tailSet methods of their underlying sets. * */ static class ConcurrentSkipListSubSet extends AbstractSet implements NavigableSet, java.io.Serializable { private static final long serialVersionUID = -7647078645896651609L; /** The underlying submap */ private final ConcurrentSkipListMap.ConcurrentSkipListSubMap s; /** * Creates a new submap. * @param fromElement inclusive least value, or null if from start * @param toElement exclusive upper bound or null if to end * @throws IllegalArgumentException if fromElement and toElement * non-null and fromElement greater than toElement */ ConcurrentSkipListSubSet(ConcurrentSkipListMap map, E fromElement, E toElement) { s = new ConcurrentSkipListMap.ConcurrentSkipListSubMap (map, fromElement, toElement); } // subsubset construction public NavigableSet subSet(E fromElement, E toElement) { if (!s.inOpenRange(fromElement) || !s.inOpenRange(toElement)) throw new IllegalArgumentException("element out of range"); return new ConcurrentSkipListSubSet(s.getMap(), fromElement, toElement); } public NavigableSet headSet(E toElement) { E least = s.getLeast(); if (!s.inOpenRange(toElement)) throw new IllegalArgumentException("element out of range"); return new ConcurrentSkipListSubSet(s.getMap(), least, toElement); } public NavigableSet tailSet(E fromElement) { E fence = s.getFence(); if (!s.inOpenRange(fromElement)) throw new IllegalArgumentException("element out of range"); return new ConcurrentSkipListSubSet(s.getMap(), fromElement, fence); } // relays to submap methods public int size() { return s.size(); } public boolean isEmpty() { return s.isEmpty(); } public boolean contains(Object o) { return s.containsKey(o); } public void clear() { s.clear(); } public E first() { return s.firstKey(); } public E last() { return s.lastKey(); } public E ceiling(E o) { return s.ceilingKey(o); } public E lower(E o) { return s.lowerKey(o); } public E floor(E o) { return s.floorKey(o); } public E higher(E o) { return s.higherKey(o); } public boolean remove(Object o) { return s.remove(o)==Boolean.TRUE; } public boolean add(E o) { return s.put(o, Boolean.TRUE)==null; } public Comparator comparator() { return s.comparator(); } public Iterator iterator() { return s.keySet().iterator(); } public Iterator descendingIterator() { return s.descendingKeySet().iterator(); } public E pollFirst() { Map.Entry e = s.pollFirstEntry(); return (e == null) ? null : e.getKey(); } public E pollLast() { Map.Entry e = s.pollLastEntry(); return (e == null) ? null : e.getKey(); } } }