src/share/classes/java/util/Collections.java
Print this page
rev 7461 : 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set}
Reviewed-by: dmocek, martin, smarks
@@ -25,10 +25,11 @@
package java.util;
import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.lang.reflect.Array;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -134,11 +135,11 @@
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
- * TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
+ * TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* <p>This implementation dumps the specified list into an array, sorts
@@ -195,11 +196,11 @@
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
- * TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
+ * TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* <p>This implementation dumps the specified list into an array, sorts
@@ -1210,10 +1211,98 @@
public E first() {return ss.first();}
public E last() {return ss.last();}
}
/**
+ * Returns an unmodifiable view of the specified navigable set. This method
+ * allows modules to provide users with "read-only" access to internal
+ * navigable sets. Query operations on the returned navigable set "read
+ * through" to the specified navigable set. Attempts to modify the returned
+ * navigable set, whether direct, via its iterator, or via its
+ * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in
+ * an {@code UnsupportedOperationException}.<p>
+ *
+ * The returned navigable set will be serializable if the specified
+ * navigable set is serializable.
+ *
+ * @param s the navigable set for which an unmodifiable view is to be
+ * returned
+ * @return an unmodifiable view of the specified navigable set
+ * @since 1.8
+ */
+ public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s) {
+ return new UnmodifiableNavigableSet<>(s);
+ }
+
+ /**
+ * Wraps a navigable set and disables all of the mutative operations.
+ *
+ * @param <E> type of elements
+ * @serial include
+ */
+ static class UnmodifiableNavigableSet<E>
+ extends UnmodifiableSortedSet<E>
+ implements NavigableSet<E>, Serializable {
+
+ private static final long serialVersionUID = -6027448201786391929L;
+
+ /**
+ * A singleton empty unmodifiable navigable set used for
+ * {@link #emptyNavigableSet()}.
+ *
+ * @param <E> type of elements, if there were any, and bounds
+ */
+ private static class EmptyNavigableSet<E> extends UnmodifiableNavigableSet<E>
+ implements Serializable {
+ private static final long serialVersionUID = -6291252904449939134L;
+
+ public EmptyNavigableSet() {
+ super(new TreeSet<E>());
+ }
+
+ private Object readResolve() { return EMPTY_NAVIGABLE_SET; }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static final NavigableSet<?> EMPTY_NAVIGABLE_SET =
+ new EmptyNavigableSet<>();
+
+ /**
+ * The instance we are protecting.
+ */
+ private final NavigableSet<E> ns;
+
+ UnmodifiableNavigableSet(NavigableSet<E> s) {super(s); ns = s;}
+
+ public E lower(E e) { return ns.lower(e); }
+ public E floor(E e) { return ns.floor(e); }
+ public E ceiling(E e) { return ns.ceiling(e); }
+ public E higher(E e) { return ns.higher(e); }
+ public E pollFirst() { throw new UnsupportedOperationException(); }
+ public E pollLast() { throw new UnsupportedOperationException(); }
+ public NavigableSet<E> descendingSet()
+ { return new UnmodifiableNavigableSet<>(ns.descendingSet()); }
+ public Iterator<E> descendingIterator()
+ { return descendingSet().iterator(); }
+
+ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+ return new UnmodifiableNavigableSet<>(
+ ns.subSet(fromElement, fromInclusive, toElement, toInclusive));
+ }
+
+ public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+ return new UnmodifiableNavigableSet<>(
+ ns.headSet(toElement, inclusive));
+ }
+
+ public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
+ return new UnmodifiableNavigableSet<>(
+ ns.tailSet(fromElement, inclusive));
+ }
+ }
+
+ /**
* Returns an unmodifiable view of the specified list. This method allows
* modules to provide users with "read-only" access to internal
* lists. Query operations on the returned list "read through" to the
* specified list, and attempts to modify the returned list, whether
* direct or via its iterator, result in an
@@ -1236,10 +1325,11 @@
* @serial include
*/
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
private static final long serialVersionUID = -283967356065247728L;
+
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
@@ -1598,11 +1688,12 @@
* Map Entry when asked to perform an equality check.
*/
private static class UnmodifiableEntry<K,V> implements Map.Entry<K,V> {
private Map.Entry<? extends K, ? extends V> e;
- UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e) {this.e = e;}
+ UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e)
+ {this.e = Objects.requireNonNull(e);}
public K getKey() {return e.getKey();}
public V getValue() {return e.getValue();}
public V setValue(V value) {
throw new UnsupportedOperationException();
@@ -1650,28 +1741,155 @@
implements SortedMap<K,V>, Serializable {
private static final long serialVersionUID = -8806743815996713206L;
private final SortedMap<K, ? extends V> sm;
- UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m;}
+ UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m; }
+ public Comparator<? super K> comparator() { return sm.comparator(); }
+ public SortedMap<K,V> subMap(K fromKey, K toKey)
+ { return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); }
+ public SortedMap<K,V> headMap(K toKey)
+ { return new UnmodifiableSortedMap<>(sm.headMap(toKey)); }
+ public SortedMap<K,V> tailMap(K fromKey)
+ { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); }
+ public K firstKey() { return sm.firstKey(); }
+ public K lastKey() { return sm.lastKey(); }
+ }
+
+ /**
+ * Returns an unmodifiable view of the specified navigable map. This method
+ * allows modules to provide users with "read-only" access to internal
+ * navigable maps. Query operations on the returned navigable map "read
+ * through" to the specified navigable map. Attempts to modify the returned
+ * navigable map, whether direct, via its collection views, or via its
+ * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in
+ * an {@code UnsupportedOperationException}.<p>
+ *
+ * The returned navigable map will be serializable if the specified
+ * navigable map is serializable.
+ *
+ * @param m the navigable map for which an unmodifiable view is to be
+ * returned
+ * @return an unmodifiable view of the specified navigable map
+ * @since 1.8
+ */
+ public static <K,V> NavigableMap<K,V> unmodifiableNavigableMap(NavigableMap<K, ? extends V> m) {
+ return new UnmodifiableNavigableMap<>(m);
+ }
+
+ /**
+ * @serial include
+ */
+ static class UnmodifiableNavigableMap<K,V>
+ extends UnmodifiableSortedMap<K,V>
+ implements NavigableMap<K,V>, Serializable {
+ private static final long serialVersionUID = -4858195264774772197L;
+
+ /**
+ * A class for the {@link EMPTY_NAVIGABLE_MAP} which needs readResolve
+ * to preserve singleton property.
+ *
+ * @param <K> type of keys, if there were any, and of bounds
+ * @param <V> type of values, if there were any
+ */
+ private static class EmptyNavigableMap<K,V> extends UnmodifiableNavigableMap<K,V>
+ implements Serializable {
+
+ private static final long serialVersionUID = -2239321462712562324L;
- public Comparator<? super K> comparator() {return sm.comparator();}
+ EmptyNavigableMap() { super(new TreeMap()); }
- public SortedMap<K,V> subMap(K fromKey, K toKey) {
- return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey));
+ @Override
+ public NavigableSet<K> navigableKeySet()
+ { return emptyNavigableSet(); }
+
+ private Object readResolve() { return EMPTY_NAVIGABLE_MAP; }
}
- public SortedMap<K,V> headMap(K toKey) {
- return new UnmodifiableSortedMap<>(sm.headMap(toKey));
+
+ /**
+ * Singleton for {@link emptyNavigableMap()} which is also immutable.
+ */
+ private static final EmptyNavigableMap<?,?> EMPTY_NAVIGABLE_MAP =
+ new EmptyNavigableMap<>();
+
+ /**
+ * The instance we wrap and protect.
+ */
+ private final NavigableMap<K, ? extends V> nm;
+
+ UnmodifiableNavigableMap(NavigableMap<K, ? extends V> m)
+ {super(m); nm = m;}
+
+ public K lowerKey(K key) { return nm.lowerKey(key); }
+ public K floorKey(K key) { return nm.floorKey(key); }
+ public K ceilingKey(K key) { return nm.ceilingKey(key); }
+ public K higherKey(K key) { return nm.higherKey(key); }
+
+ public Entry<K, V> lowerEntry(K key) {
+ Entry<K,V> lower = (Entry<K, V>) nm.lowerEntry(key);
+ return (null != lower)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(lower)
+ : null;
}
- public SortedMap<K,V> tailMap(K fromKey) {
- return new UnmodifiableSortedMap<>(sm.tailMap(fromKey));
+
+ public Entry<K, V> floorEntry(K key) {
+ Entry<K,V> floor = (Entry<K, V>) nm.floorEntry(key);
+ return (null != floor)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(floor)
+ : null;
+ }
+
+ public Entry<K, V> ceilingEntry(K key) {
+ Entry<K,V> ceiling = (Entry<K, V>) nm.ceilingEntry(key);
+ return (null != ceiling)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(ceiling)
+ : null;
+ }
+
+
+ public Entry<K, V> higherEntry(K key) {
+ Entry<K,V> higher = (Entry<K, V>) nm.higherEntry(key);
+ return (null != higher)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(higher)
+ : null;
+ }
+
+ public Entry<K, V> firstEntry() {
+ Entry<K,V> first = (Entry<K, V>) nm.firstEntry();
+ return (null != first)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(first)
+ : null;
}
- public K firstKey() {return sm.firstKey();}
- public K lastKey() {return sm.lastKey();}
+ public Entry<K, V> lastEntry() {
+ Entry<K,V> last = (Entry<K, V>) nm.lastEntry();
+ return (null != last)
+ ? new UnmodifiableEntrySet.UnmodifiableEntry(last)
+ : null;
}
+ public Entry<K, V> pollFirstEntry()
+ { throw new UnsupportedOperationException(); }
+ public Entry<K, V> pollLastEntry()
+ { throw new UnsupportedOperationException(); }
+ public NavigableMap<K, V> descendingMap()
+ { return unmodifiableNavigableMap(nm.descendingMap()); }
+ public NavigableSet<K> navigableKeySet()
+ { return unmodifiableNavigableSet(nm.navigableKeySet()); }
+ public NavigableSet<K> descendingKeySet()
+ { return unmodifiableNavigableSet(nm.descendingKeySet()); }
+
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ return unmodifiableNavigableMap(
+ nm.subMap(fromKey, fromInclusive, toKey, toInclusive));
+ }
+
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive)
+ { return unmodifiableNavigableMap(nm.headMap(toKey, inclusive)); }
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive)
+ { return unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive)); }
+ }
// Synch Wrappers
/**
* Returns a synchronized (thread-safe) collection backed by the specified
@@ -1721,18 +1939,17 @@
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
- if (c==null)
- throw new NullPointerException();
- this.c = c;
+ this.c = Objects.requireNonNull(c);
mutex = this;
}
+
SynchronizedCollection(Collection<E> c, Object mutex) {
- this.c = c;
- this.mutex = mutex;
+ this.c = Objects.requireNonNull(c);
+ this.mutex = Objects.requireNonNull(mutex);
}
public int size() {
synchronized (mutex) {return c.size();}
}
@@ -1943,10 +2160,124 @@
synchronized (mutex) {return ss.last();}
}
}
/**
+ * Returns a synchronized (thread-safe) navigable set backed by the
+ * specified navigable set. In order to guarantee serial access, it is
+ * critical that <strong>all</strong> access to the backing navigable set is
+ * accomplished through the returned navigable set (or its views).<p>
+ *
+ * It is imperative that the user manually synchronize on the returned
+ * navigable set when iterating over it or any of its {@code subSet},
+ * {@code headSet}, or {@code tailSet} views.
+ * <pre>
+ * NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet());
+ * ...
+ * synchronized (s) {
+ * Iterator i = s.iterator(); // Must be in the synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre>
+ * or:
+ * <pre>
+ * NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet());
+ * NavigableSet s2 = s.headSet(foo, true);
+ * ...
+ * synchronized (s) { // Note: s, not s2!!!
+ * Iterator i = s2.iterator(); // Must be in the synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre>
+ * Failure to follow this advice may result in non-deterministic behavior.
+ *
+ * <p>The returned navigable set will be serializable if the specified
+ * navigable set is serializable.
+ *
+ * @param s the navigable set to be "wrapped" in a synchronized navigable
+ * set
+ * @return a synchronized view of the specified navigable set
+ * @since 1.8
+ */
+ public static <T> NavigableSet<T> synchronizedNavigableSet(NavigableSet<T> s) {
+ return new SynchronizedNavigableSet<>(s);
+ }
+
+ /**
+ * @serial include
+ */
+ static class SynchronizedNavigableSet<E>
+ extends SynchronizedSortedSet<E>
+ implements NavigableSet<E>
+ {
+ private static final long serialVersionUID = -5505529816273629798L;
+
+ private final NavigableSet<E> ns;
+
+ SynchronizedNavigableSet(NavigableSet<E> s) {
+ super(s);
+ ns = s;
+ }
+
+ SynchronizedNavigableSet(NavigableSet<E> s, Object mutex) {
+ super(s, mutex);
+ ns = s;
+ }
+ public E lower(E e) { synchronized (mutex) {return ns.lower(e);} }
+ public E floor(E e) { synchronized (mutex) {return ns.floor(e);} }
+ public E ceiling(E e) { synchronized (mutex) {return ns.ceiling(e);} }
+ public E higher(E e) { synchronized (mutex) {return ns.higher(e);} }
+ public E pollFirst() { synchronized (mutex) {return ns.pollFirst();} }
+ public E pollLast() { synchronized (mutex) {return ns.pollLast();} }
+
+ public NavigableSet<E> descendingSet() {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex);
+ }
+ }
+
+ public Iterator<E> descendingIterator()
+ { synchronized (mutex) { return descendingSet().iterator(); } }
+
+ public NavigableSet<E> subSet(E fromElement, E toElement) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.subSet(fromElement, true, toElement, false), mutex);
+ }
+ }
+ public NavigableSet<E> headSet(E toElement) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.headSet(toElement, false), mutex);
+ }
+ }
+ public NavigableSet<E> tailSet(E fromElement) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet(ns.tailSet(fromElement, true), mutex);
+ }
+ }
+
+ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex);
+ }
+ }
+
+ public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex);
+ }
+ }
+
+ public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive));
+ }
+ }
+ }
+
+ /**
* Returns a synchronized (thread-safe) list backed by the specified
* list. In order to guarantee serial access, it is critical that
* <strong>all</strong> access to the backing list is accomplished
* through the returned list.<p>
*
@@ -2151,13 +2482,11 @@
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
- if (m==null)
- throw new NullPointerException();
- this.m = m;
+ this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
@@ -2332,11 +2661,10 @@
*/
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) {
return new SynchronizedSortedMap<>(m);
}
-
/**
* @serial include
*/
static class SynchronizedSortedMap<K,V>
extends SynchronizedMap<K,V>
@@ -2382,10 +2710,168 @@
public K lastKey() {
synchronized (mutex) {return sm.lastKey();}
}
}
+ /**
+ * Returns a synchronized (thread-safe) navigable map backed by the
+ * specified navigable map. In order to guarantee serial access, it is
+ * critical that <strong>all</strong> access to the backing navigable map is
+ * accomplished through the returned navigable map (or its views).<p>
+ *
+ * It is imperative that the user manually synchronize on the returned
+ * navigable map when iterating over any of its collection views, or the
+ * collections views of any of its {@code subMap}, {@code headMap} or
+ * {@code tailMap} views.
+ * <pre>
+ * NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
+ * ...
+ * Set s = m.keySet(); // Needn't be in synchronized block
+ * ...
+ * synchronized (m) { // Synchronizing on m, not s!
+ * Iterator i = s.iterator(); // Must be in synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre>
+ * or:
+ * <pre>
+ * NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
+ * NavigableMap m2 = m.subMap(foo, true, bar, false);
+ * ...
+ * Set s2 = m2.keySet(); // Needn't be in synchronized block
+ * ...
+ * synchronized (m) { // Synchronizing on m, not m2 or s2!
+ * Iterator i = s.iterator(); // Must be in synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre>
+ * Failure to follow this advice may result in non-deterministic behavior.
+ *
+ * <p>The returned navigable map will be serializable if the specified
+ * navigable map is serializable.
+ *
+ * @param m the navigable map to be "wrapped" in a synchronized navigable
+ * map
+ * @return a synchronized view of the specified navigable map.
+ * @since 1.8
+ */
+ public static <K,V> NavigableMap<K,V> synchronizedNavigableMap(NavigableMap<K,V> m) {
+ return new SynchronizedNavigableMap<>(m);
+ }
+
+ /**
+ * A synchronized NavigableMap.
+ *
+ * @serial include
+ */
+ static class SynchronizedNavigableMap<K,V>
+ extends SynchronizedSortedMap<K,V>
+ implements NavigableMap<K,V>
+ {
+ private static final long serialVersionUID = 699392247599746807L;
+
+ private final NavigableMap<K,V> nm;
+
+ SynchronizedNavigableMap(NavigableMap<K,V> m) {
+ super(m);
+ nm = m;
+ }
+ SynchronizedNavigableMap(NavigableMap<K,V> m, Object mutex) {
+ super(m, mutex);
+ nm = m;
+ }
+
+ public Entry<K, V> lowerEntry(K key)
+ { synchronized (mutex) { return nm.lowerEntry(key); } }
+ public K lowerKey(K key)
+ { synchronized (mutex) { return nm.lowerKey(key); } }
+ public Entry<K, V> floorEntry(K key)
+ { synchronized (mutex) { return nm.floorEntry(key); } }
+ public K floorKey(K key)
+ { synchronized (mutex) { return nm.floorKey(key); } }
+ public Entry<K, V> ceilingEntry(K key)
+ { synchronized (mutex) { return nm.ceilingEntry(key); } }
+ public K ceilingKey(K key)
+ { synchronized (mutex) { return nm.ceilingKey(key); } }
+ public Entry<K, V> higherEntry(K key)
+ { synchronized (mutex) { return nm.higherEntry(key); } }
+ public K higherKey(K key)
+ { synchronized (mutex) { return nm.higherKey(key); } }
+ public Entry<K, V> firstEntry()
+ { synchronized (mutex) { return nm.firstEntry(); } }
+ public Entry<K, V> lastEntry()
+ { synchronized (mutex) { return nm.lastEntry(); } }
+ public Entry<K, V> pollFirstEntry()
+ { synchronized (mutex) { return nm.pollFirstEntry(); } }
+ public Entry<K, V> pollLastEntry()
+ { synchronized (mutex) { return nm.pollLastEntry(); } }
+
+ public NavigableMap<K, V> descendingMap() {
+ synchronized (mutex) {
+ return
+ new SynchronizedNavigableMap(nm.descendingMap(), mutex);
+ }
+ }
+
+ public NavigableSet<K> keySet() {
+ return navigableKeySet();
+ }
+
+ public NavigableSet<K> navigableKeySet() {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex);
+ }
+ }
+
+ public NavigableSet<K> descendingKeySet() {
+ synchronized (mutex) {
+ return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex);
+ }
+ }
+
+
+ public SortedMap<K,V> subMap(K fromKey, K toKey) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap<>(
+ nm.subMap(fromKey, true, toKey, false), mutex);
+ }
+ }
+ public SortedMap<K,V> headMap(K toKey) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap<>(nm.headMap(toKey, false), mutex);
+ }
+ }
+ public SortedMap<K,V> tailMap(K fromKey) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap<>(nm.tailMap(fromKey, true),mutex);
+ }
+ }
+
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap(
+ nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex);
+ }
+ }
+
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap(
+ nm.headMap(toKey, inclusive), mutex);
+ }
+ }
+
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+ synchronized (mutex) {
+ return new SynchronizedNavigableMap(
+ nm.tailMap(fromKey, inclusive), mutex);
+ }
+ }
+ }
+
// Dynamically typesafe collection wrappers
/**
* Returns a dynamically typesafe view of the specified collection.
* Any attempt to insert an element of the wrong type will result in an
@@ -2413,16 +2899,16 @@
* as to the real source of the problem. If the problem is reproducible,
* one can quickly determine its source by temporarily modifying the
* program to wrap the collection with a dynamically typesafe view.
* For example, this declaration:
* <pre> {@code
- * Collection<String> c = new HashSet<String>();
+ * Collection<String> c = new HashSet<>();
* }</pre>
* may be replaced temporarily by this one:
* <pre> {@code
* Collection<String> c = Collections.checkedCollection(
- * new HashSet<String>(), String.class);
+ * new HashSet<>(), String.class);
* }</pre>
* Running the program again will cause it to fail at the point where
* an incorrectly typed element is inserted into the collection, clearly
* identifying the source of the problem. Once the problem is fixed, the
* modified declaration may be reverted back to the original.
@@ -2704,10 +3190,11 @@
*/
static class CheckedSortedSet<E> extends CheckedSet<E>
implements SortedSet<E>, Serializable
{
private static final long serialVersionUID = 1599911165492914959L;
+
private final SortedSet<E> ss;
CheckedSortedSet(SortedSet<E> s, Class<E> type) {
super(s, type);
ss = s;
@@ -2726,10 +3213,91 @@
public SortedSet<E> tailSet(E fromElement) {
return checkedSortedSet(ss.tailSet(fromElement), type);
}
}
+/**
+ * Returns a dynamically typesafe view of the specified navigable set.
+ * Any attempt to insert an element of the wrong type will result in an
+ * immediate {@link ClassCastException}. Assuming a navigable set
+ * contains no incorrectly typed elements prior to the time a
+ * dynamically typesafe view is generated, and that all subsequent
+ * access to the navigable set takes place through the view, it is
+ * <em>guaranteed</em> that the navigable set cannot contain an incorrectly
+ * typed element.
+ *
+ * <p>A discussion of the use of dynamically typesafe views may be
+ * found in the documentation for the {@link #checkedCollection
+ * checkedCollection} method.
+ *
+ * <p>The returned navigable set will be serializable if the specified
+ * navigable set is serializable.
+ *
+ * <p>Since {@code null} is considered to be a value of any reference
+ * type, the returned navigable set permits insertion of null elements
+ * whenever the backing sorted set does.
+ *
+ * @param s the navigable set for which a dynamically typesafe view is to be
+ * returned
+ * @param type the type of element that {@code s} is permitted to hold
+ * @return a dynamically typesafe view of the specified navigable set
+ * @since 1.8
+ */
+ public static <E> NavigableSet<E> checkedNavigableSet(NavigableSet<E> s,
+ Class<E> type) {
+ return new CheckedNavigableSet<>(s, type);
+ }
+
+ /**
+ * @serial include
+ */
+ static class CheckedNavigableSet<E> extends CheckedSortedSet<E>
+ implements NavigableSet<E>, Serializable
+ {
+ private static final long serialVersionUID = -5429120189805438922L;
+
+ private final NavigableSet<E> ns;
+
+ CheckedNavigableSet(NavigableSet<E> s, Class<E> type) {
+ super(s, type);
+ ns = s;
+ }
+
+ public E lower(E e) { return ns.lower(e); }
+ public E floor(E e) { return ns.floor(e); }
+ public E ceiling(E e) { return ns.ceiling(e); }
+ public E higher(E e) { return ns.higher(e); }
+ public E pollFirst() { return ns.pollFirst(); }
+ public E pollLast() {return ns.pollLast(); }
+ public NavigableSet<E> descendingSet()
+ { return checkedNavigableSet(ns.descendingSet(), type); }
+ public Iterator<E> descendingIterator()
+ {return checkedNavigableSet(ns.descendingSet(), type).iterator(); }
+
+ public NavigableSet<E> subSet(E fromElement, E toElement) {
+ return checkedNavigableSet(ns.subSet(fromElement, true, toElement, false), type);
+ }
+ public NavigableSet<E> headSet(E toElement) {
+ return checkedNavigableSet(ns.headSet(toElement, false), type);
+ }
+ public NavigableSet<E> tailSet(E fromElement) {
+ return checkedNavigableSet(ns.tailSet(fromElement, true), type);
+ }
+
+ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+ return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type);
+ }
+
+ public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+ return checkedNavigableSet(ns.headSet(toElement, inclusive), type);
+ }
+
+ public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
+ return checkedNavigableSet(ns.tailSet(fromElement, inclusive), type);
+ }
+ }
+
/**
* Returns a dynamically typesafe view of the specified list.
* Any attempt to insert an element of the wrong type will result in
* an immediate {@link ClassCastException}. Assuming a list contains
* no incorrectly typed elements prior to the time a dynamically typesafe
@@ -2938,15 +3506,13 @@
return "Attempt to insert " + value.getClass() +
" value into map with value type " + valueType;
}
CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) {
- if (m == null || keyType == null || valueType == null)
- throw new NullPointerException();
- this.m = m;
- this.keyType = keyType;
- this.valueType = valueType;
+ this.m = Objects.requireNonNull(m);
+ this.keyType = Objects.requireNonNull(keyType);
+ this.valueType = Objects.requireNonNull(valueType);
}
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean containsKey(Object key) { return m.containsKey(key); }
@@ -3219,12 +3785,12 @@
private static class CheckedEntry<K,V,T> implements Map.Entry<K,V> {
private final Map.Entry<K, V> e;
private final Class<T> valueType;
CheckedEntry(Map.Entry<K, V> e, Class<T> valueType) {
- this.e = e;
- this.valueType = valueType;
+ this.e = Objects.requireNonNull(e);
+ this.valueType = Objects.requireNonNull(valueType);
}
public K getKey() { return e.getKey(); }
public V getValue() { return e.getValue(); }
public int hashCode() { return e.hashCode(); }
@@ -3323,10 +3889,181 @@
public SortedMap<K,V> tailMap(K fromKey) {
return checkedSortedMap(sm.tailMap(fromKey), keyType, valueType);
}
}
+ /**
+ * Returns a dynamically typesafe view of the specified navigable map.
+ * Any attempt to insert a mapping whose key or value have the wrong
+ * type will result in an immediate {@link ClassCastException}.
+ * Similarly, any attempt to modify the value currently associated with
+ * a key will result in an immediate {@link ClassCastException},
+ * whether the modification is attempted directly through the map
+ * itself, or through a {@link Map.Entry} instance obtained from the
+ * map's {@link Map#entrySet() entry set} view.
+ *
+ * <p>Assuming a map contains no incorrectly typed keys or values
+ * prior to the time a dynamically typesafe view is generated, and
+ * that all subsequent access to the map takes place through the view
+ * (or one of its collection views), it is <em>guaranteed</em> that the
+ * map cannot contain an incorrectly typed key or value.
+ *
+ * <p>A discussion of the use of dynamically typesafe views may be
+ * found in the documentation for the {@link #checkedCollection
+ * checkedCollection} method.
+ *
+ * <p>The returned map will be serializable if the specified map is
+ * serializable.
+ *
+ * <p>Since {@code null} is considered to be a value of any reference
+ * type, the returned map permits insertion of null keys or values
+ * whenever the backing map does.
+ *
+ * @param <K> type of map keys
+ * @param <V> type of map values
+ * @param m the map for which a dynamically typesafe view is to be
+ * returned
+ * @param keyType the type of key that {@code m} is permitted to hold
+ * @param valueType the type of value that {@code m} is permitted to hold
+ * @return a dynamically typesafe view of the specified map
+ * @since 1.8
+ */
+ public static <K,V> NavigableMap<K,V> checkedNavigableMap(NavigableMap<K, V> m,
+ Class<K> keyType,
+ Class<V> valueType) {
+ return new CheckedNavigableMap<>(m, keyType, valueType);
+ }
+
+ /**
+ * @serial include
+ */
+ static class CheckedNavigableMap<K,V> extends CheckedSortedMap<K,V>
+ implements NavigableMap<K,V>, Serializable
+ {
+ private static final long serialVersionUID = -4852462692372534096L;
+
+ private final NavigableMap<K, V> nm;
+
+ CheckedNavigableMap(NavigableMap<K, V> m,
+ Class<K> keyType, Class<V> valueType) {
+ super(m, keyType, valueType);
+ nm = m;
+ }
+
+ public Comparator<? super K> comparator() { return nm.comparator(); }
+ public K firstKey() { return nm.firstKey(); }
+ public K lastKey() { return nm.lastKey(); }
+
+ public Entry<K, V> lowerEntry(K key) {
+ Entry<K,V> lower = nm.lowerEntry(key);
+ return (null != lower)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(lower, valueType)
+ : null;
+ }
+
+ public K lowerKey(K key) { return nm.lowerKey(key); }
+
+ public Entry<K, V> floorEntry(K key) {
+ Entry<K,V> floor = nm.floorEntry(key);
+ return (null != floor)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(floor, valueType)
+ : null;
+ }
+
+ public K floorKey(K key) { return nm.floorKey(key); }
+
+ public Entry<K, V> ceilingEntry(K key) {
+ Entry<K,V> ceiling = nm.ceilingEntry(key);
+ return (null != ceiling)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(ceiling, valueType)
+ : null;
+ }
+
+ public K ceilingKey(K key) { return nm.ceilingKey(key); }
+
+ public Entry<K, V> higherEntry(K key) {
+ Entry<K,V> higher = nm.higherEntry(key);
+ return (null != higher)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(higher, valueType)
+ : null;
+ }
+
+ public K higherKey(K key) { return nm.higherKey(key); }
+
+ public Entry<K, V> firstEntry() {
+ Entry<K,V> first = nm.firstEntry();
+ return (null != first)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(first, valueType)
+ : null;
+ }
+
+ public Entry<K, V> lastEntry() {
+ Entry<K,V> last = nm.lastEntry();
+ return (null != last)
+ ? new CheckedMap.CheckedEntrySet.CheckedEntry(last, valueType)
+ : null;
+ }
+
+ public Entry<K, V> pollFirstEntry() {
+ Entry<K,V> entry = nm.pollFirstEntry();
+ return (null == entry)
+ ? null
+ : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType);
+ }
+
+ public Entry<K, V> pollLastEntry() {
+ Entry<K,V> entry = nm.pollLastEntry();
+ return (null == entry)
+ ? null
+ : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType);
+ }
+
+ public NavigableMap<K, V> descendingMap() {
+ return checkedNavigableMap(nm.descendingMap(), keyType, valueType);
+ }
+
+ public NavigableSet<K> keySet() {
+ return navigableKeySet();
+ }
+
+ public NavigableSet<K> navigableKeySet() {
+ return checkedNavigableSet(nm.navigableKeySet(), keyType);
+ }
+
+ public NavigableSet<K> descendingKeySet() {
+ return checkedNavigableSet(nm.descendingKeySet(), keyType);
+ }
+
+ @Override
+ public NavigableMap<K,V> subMap(K fromKey, K toKey) {
+ return checkedNavigableMap(nm.subMap(fromKey, true, toKey, false),
+ keyType, valueType);
+ }
+
+ @Override
+ public NavigableMap<K,V> headMap(K toKey) {
+ return checkedNavigableMap(nm.headMap(toKey, false), keyType, valueType);
+ }
+
+ @Override
+ public NavigableMap<K,V> tailMap(K fromKey) {
+ return checkedNavigableMap(nm.tailMap(fromKey, true), keyType, valueType);
+ }
+
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType);
+ }
+
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+ return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType);
+ }
+
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+ return checkedNavigableMap(nm.tailMap(fromKey, inclusive), keyType, valueType);
+ }
+ }
+
// Empty collections
/**
* Returns an iterator that has no elements. More precisely,
*
@@ -3344,10 +4081,11 @@
* </ul>
*
* <p>Implementations of this method are permitted, but not
* required, to return the same object from multiple invocations.
*
+ * @param <T> type of elements, if there were any, in the iterator
* @return an empty iterator
* @since 1.7
*/
@SuppressWarnings("unchecked")
public static <T> Iterator<T> emptyIterator() {
@@ -3394,10 +4132,11 @@
* </ul>
*
* <p>Implementations of this method are permitted, but not
* required, to return the same object from multiple invocations.
*
+ * @param <T> type of elements, if there were any, in the iterator
* @return an empty list iterator
* @since 1.7
*/
@SuppressWarnings("unchecked")
public static <T> ListIterator<T> emptyListIterator() {
@@ -3458,21 +4197,23 @@
*/
@SuppressWarnings("rawtypes")
public static final Set EMPTY_SET = new EmptySet<>();
/**
- * Returns the empty set (immutable). This set is serializable.
+ * Returns an empty set (immutable). This set is serializable.
* Unlike the like-named field, this method is parameterized.
*
* <p>This example illustrates the type-safe way to obtain an empty set:
* <pre>
* Set<String> s = Collections.emptySet();
* </pre>
- * Implementation note: Implementations of this method need not
- * create a separate <tt>Set</tt> object for each call. Using this
- * method is likely to have comparable cost to using the like-named
- * field. (Unlike this method, the field does not provide type safety.)
+ * @implNote Implementations of this method need not create a separate
+ * {@code Set} object for each call. Using this method is likely to have
+ * comparable cost to using the like-named field. (Unlike this method, the
+ * field does not provide type safety.)
+ *
+ * @return the empty set
*
* @see #EMPTY_SET
* @since 1.5
*/
@SuppressWarnings("unchecked")
@@ -3523,125 +4264,49 @@
return EMPTY_SET;
}
}
/**
- * Returns the empty sorted set (immutable). This set is serializable.
+ * Returns an empty sorted set (immutable). This set is serializable.
*
- * <p>This example illustrates the type-safe way to obtain an empty sorted
- * set:
- * <pre>
- * SortedSet<String> s = Collections.emptySortedSet();
- * </pre>
- * Implementation note: Implementations of this method need not
- * create a separate <tt>SortedSet</tt> object for each call.
+ * <p>This example illustrates the type-safe way to obtain an empty
+ * sorted set:
+ * <pre> {@code
+ * SortedSet<String> s = Collections.emptySortedSet();
+ * }</pre>
+ *
+ * @implNote Implementations of this method need not create a separate
+ * {@code SortedSet} object for each call.
*
+ * @param <E> type of elements, if there were any, in the set
+ * @return the empty sorted set
* @since 1.8
*/
@SuppressWarnings("unchecked")
- public static final <E> SortedSet<E> emptySortedSet() {
- return (SortedSet<E>) new EmptySortedSet<>();
+ public static <E> SortedSet<E> emptySortedSet() {
+ return (SortedSet<E>) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET;
}
/**
- * @serial include
+ * Returns an empty navigable set (immutable). This set is serializable.
+ *
+ * <p>This example illustrates the type-safe way to obtain an empty
+ * navigable set:
+ * <pre> {@code
+ * NavigableSet<String> s = Collections.emptyNavigableSet();
+ * }</pre>
+ *
+ * @implNote Implementations of this method need not
+ * create a separate {@code NavigableSet} object for each call.
+ *
+ * @param <E> type of elements, if there were any, in the set
+ * @return the empty navigable set
+ * @since 1.8
*/
- private static class EmptySortedSet<E>
- extends AbstractSet<E>
- implements SortedSet<E>, Serializable
- {
- private static final long serialVersionUID = 6316515401502265487L;
- public Iterator<E> iterator() { return emptyIterator(); }
- public int size() {return 0;}
- public boolean isEmpty() {return true;}
- public boolean contains(Object obj) {return false;}
- public boolean containsAll(Collection<?> c) { return c.isEmpty(); }
- public Object[] toArray() { return new Object[0]; }
-
- public <E> E[] toArray(E[] a) {
- if (a.length > 0)
- a[0] = null;
- return a;
- }
-
- // Preserves singleton property
- private Object readResolve() {
- return new EmptySortedSet<>();
- }
-
- @Override
- public Comparator<? super E> comparator() {
- return null;
- }
-
- @Override
@SuppressWarnings("unchecked")
- public SortedSet<E> subSet(Object fromElement, Object toElement) {
- Objects.requireNonNull(fromElement);
- Objects.requireNonNull(toElement);
-
- if (!(fromElement instanceof Comparable) ||
- !(toElement instanceof Comparable))
- {
- throw new ClassCastException();
- }
-
- if ((((Comparable)fromElement).compareTo(toElement) >= 0) ||
- (((Comparable)toElement).compareTo(fromElement) < 0))
- {
- throw new IllegalArgumentException();
- }
-
- return emptySortedSet();
- }
-
- @Override
- public SortedSet<E> headSet(Object toElement) {
- Objects.requireNonNull(toElement);
-
- if (!(toElement instanceof Comparable)) {
- throw new ClassCastException();
- }
-
- return emptySortedSet();
- }
-
- @Override
- public SortedSet<E> tailSet(Object fromElement) {
- Objects.requireNonNull(fromElement);
-
- if (!(fromElement instanceof Comparable)) {
- throw new ClassCastException();
- }
-
- return emptySortedSet();
- }
-
- @Override
- public E first() {
- throw new NoSuchElementException();
- }
-
- @Override
- public E last() {
- throw new NoSuchElementException();
- }
-
- // Override default methods in Collection
- @Override
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- }
-
- @Override
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return false;
- }
-
- @Override
- public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
+ public static <E> NavigableSet<E> emptyNavigableSet() {
+ return (NavigableSet<E>) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET;
}
/**
* The empty list (immutable). This list is serializable.
*
@@ -3649,21 +4314,24 @@
*/
@SuppressWarnings("rawtypes")
public static final List EMPTY_LIST = new EmptyList<>();
/**
- * Returns the empty list (immutable). This list is serializable.
+ * Returns an empty list (immutable). This list is serializable.
*
* <p>This example illustrates the type-safe way to obtain an empty list:
* <pre>
* List<String> s = Collections.emptyList();
* </pre>
* Implementation note: Implementations of this method need not
* create a separate <tt>List</tt> object for each call. Using this
* method is likely to have comparable cost to using the like-named
* field. (Unlike this method, the field does not provide type safety.)
*
+ * @param <T> type of elements, if there were any, in the list
+ * @return an empty immutable list
+ *
* @see #EMPTY_LIST
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
@@ -3746,30 +4414,69 @@
*/
@SuppressWarnings("rawtypes")
public static final Map EMPTY_MAP = new EmptyMap<>();
/**
- * Returns the empty map (immutable). This map is serializable.
+ * Returns an empty map (immutable). This map is serializable.
*
- * <p>This example illustrates the type-safe way to obtain an empty set:
+ * <p>This example illustrates the type-safe way to obtain an empty map:
* <pre>
* Map<String, Date> s = Collections.emptyMap();
* </pre>
- * Implementation note: Implementations of this method need not
- * create a separate <tt>Map</tt> object for each call. Using this
- * method is likely to have comparable cost to using the like-named
- * field. (Unlike this method, the field does not provide type safety.)
+ * @implNote Implementations of this method need not create a separate
+ * {@code Map} object for each call. Using this method is likely to have
+ * comparable cost to using the like-named field. (Unlike this method, the
+ * field does not provide type safety.)
*
+ * @return an empty map
* @see #EMPTY_MAP
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <K,V> Map<K,V> emptyMap() {
return (Map<K,V>) EMPTY_MAP;
}
/**
+ * Returns an empty sorted map (immutable). This map is serializable.
+ *
+ * <p>This example illustrates the type-safe way to obtain an empty map:
+ * <pre> {@code
+ * SortedMap<String, Date> s = Collections.emptySortedMap();
+ * }</pre>
+ *
+ * @implNote Implementations of this method need not create a separate
+ * {@code SortedMap} object for each call.
+ *
+ * @return an empty sorted map
+ * @since 1.8
+ */
+ @SuppressWarnings("unchecked")
+ public static final <K,V> SortedMap<K,V> emptySortedMap() {
+ return (SortedMap<K,V>) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP;
+ }
+
+ /**
+ * Returns an empty navigable map (immutable). This map is serializable.
+ *
+ * <p>This example illustrates the type-safe way to obtain an empty map:
+ * <pre> {@code
+ * NavigableMap<String, Date> s = Collections.emptyNavigableMap();
+ * }</pre>
+ *
+ * @implNote Implementations of this method need not create a separate
+ * {@code NavigableMap} object for each call.
+ *
+ * @return an empty navigable map
+ * @since 1.8
+ */
+ @SuppressWarnings("unchecked")
+ public static final <K,V> NavigableMap<K,V> emptyNavigableMap() {
+ return (NavigableMap<K,V>) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP;
+ }
+
+ /**
* @serial include
*/
private static class EmptyMap<K,V>
extends AbstractMap<K,V>
implements Serializable
@@ -4070,17 +4777,13 @@
k = key;
v = value;
}
public int size() {return 1;}
-
public boolean isEmpty() {return false;}
-
public boolean containsKey(Object key) {return eq(key, k);}
-
public boolean containsValue(Object value) {return eq(value, v);}
-
public V get(Object key) {return (eq(key, k) ? v : null);}
private transient Set<K> keySet = null;
private transient Set<Map.Entry<K,V>> entrySet = null;
private transient Collection<V> values = null;
@@ -4414,10 +5117,12 @@
return l;
}
/**
* Returns true if the specified arguments are equal, or both null.
+ *
+ * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
*/
static boolean eq(Object o1, Object o2) {
return o1==null ? o2==null : o1.equals(o2);
}