src/share/classes/java/util/Collections.java
Print this page
rev 7291 : 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set}
Reviewed-by: dmocek
*** 25,34 ****
--- 25,35 ----
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,144 ****
* 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
* 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
--- 135,145 ----
* 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 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,205 ****
* 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
* 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
--- 196,206 ----
* 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 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,1219 ****
--- 1211,1286 ----
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
+ * <tt>subSet</tt>, <tt>headSet</tt>, or <tt>tailSet</tt> views, result in
+ * an <tt>UnsupportedOperationException</tt>.<p>
+ *
+ * The returned navigable set will be serializable if the specified sorted 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);
+ }
+
+ /**
+ * @serial include
+ */
+ static class UnmodifiableNavigableSet<E>
+ extends UnmodifiableSortedSet<E>
+ implements NavigableSet<E>, Serializable {
+
+ private static final long serialVersionUID = -6027448201786391929L;
+
+ private final NavigableSet<E> ns;
+
+ UnmodifiableNavigableSet(NavigableSet<E> s) {super(s); ns = s;}
+
+ @Override public E lower(E e) { return ns.lower(e); }
+ @Override public E floor(E e) { return ns.floor(e); }
+ @Override public E ceiling(E e) { return ns.ceiling(e); }
+ @Override public E higher(E e) { return ns.higher(e); }
+ @Override public E pollFirst() { throw new UnsupportedOperationException(); }
+ @Override public E pollLast() { throw new UnsupportedOperationException(); }
+ @Override
+ public NavigableSet<E> descendingSet() {
+ return new UnmodifiableNavigableSet<>(ns.descendingSet());
+ }
+
+ @Override
+ public Iterator<E> descendingIterator() {
+ return descendingSet().iterator();
+ }
+
+ @Override
+ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+ return new UnmodifiableNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive));
+ }
+
+ @Override
+ public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+ return new UnmodifiableNavigableSet<>(ns.headSet(toElement, inclusive));
+ }
+
+ @Override
+ 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,1245 ****
--- 1303,1313 ----
* @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;
*** 1668,1677 ****
--- 1736,1870 ----
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
+ * <tt>subMap</tt>, <tt>headMap</tt>, or <tt>tailMap</tt> views, result in
+ * an <tt>UnsupportedOperationException</tt>.<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;
+
+ private final NavigableMap<K, ? extends V> nm;
+
+ UnmodifiableNavigableMap(NavigableMap<K, ? extends V> m) {super(m); nm = m;}
+
+ @Override
+ public Entry<K, V> lowerEntry(K key) {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lowerEntry(key));
+ }
+
+ @Override
+ public K lowerKey(K key) {
+ return nm.lowerKey(key);
+ }
+
+ @Override
+ public Entry<K, V> floorEntry(K key) {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.floorEntry(key));
+ }
+
+ @Override
+ public K floorKey(K key) {
+ return nm.floorKey(key);
+ }
+
+ @Override
+ public Entry<K, V> ceilingEntry(K key) {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.ceilingEntry(key));
+ }
+
+ @Override
+ public K ceilingKey(K key) {
+ return nm.ceilingKey(key);
+ }
+
+ @Override
+ public Entry<K, V> higherEntry(K key) {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.higherEntry(key));
+ }
+
+ @Override
+ public K higherKey(K key) {
+ return nm.higherKey(key);
+ }
+
+ @Override
+ public Entry<K, V> firstEntry() {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.firstEntry());
+ }
+
+ @Override
+ public Entry<K, V> lastEntry() {
+ return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lastEntry());
+ }
+
+ @Override
+ public Entry<K, V> pollFirstEntry() {
+ Entry<K,V> entry = (Entry<K,V>) nm.pollFirstEntry();
+ return (null == entry) ? null : new UnmodifiableEntrySet.UnmodifiableEntry(entry);
+ }
+
+ @Override
+ public Entry<K, V> pollLastEntry() {
+ Entry<K,V> entry = (Entry<K,V>) nm.pollLastEntry();
+ return (null == entry) ? null : new UnmodifiableEntrySet.UnmodifiableEntry(entry);
+ }
+
+ @Override
+ public NavigableMap<K, V> descendingMap() {
+ return unmodifiableNavigableMap(nm.descendingMap());
+ }
+
+ @Override
+ public NavigableSet<K> navigableKeySet() {
+ return unmodifiableNavigableSet(nm.navigableKeySet());
+ }
+
+ @Override
+ public NavigableSet<K> descendingKeySet() {
+ return unmodifiableNavigableSet(nm.descendingKeySet());
+ }
+
+ @Override
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ return unmodifiableNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive));
+ }
+
+ @Override
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+ return unmodifiableNavigableMap(nm.headMap(toKey, inclusive));
+ }
+
+ @Override
+ 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,1738 ****
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;
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
! this.c = c;
! this.mutex = mutex;
}
public int size() {
synchronized (mutex) {return c.size();}
}
--- 1914,1930 ----
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
! this.c = Objects.requireNonNull(c);
mutex = this;
}
+
SynchronizedCollection(Collection<E> c, Object mutex) {
! this.c = Objects.requireNonNull(c);
! this.mutex = Objects.requireNonNull(mutex);
}
public int size() {
synchronized (mutex) {return c.size();}
}
*** 1943,1952 ****
--- 2135,2242 ----
synchronized (mutex) {return ss.last();}
}
}
/**
+ * Returns a synchronized (thread-safe) sorted set backed by the specified
+ * navigable set. In order to guarantee serial access, it is critical that
+ * <strong>all</strong> access to the backing sorted 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>
+ * SortedSet 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>
+ * SortedSet s = Collections.synchronizedNavigableSet(new TreeSet());
+ * SortedSet s2 = s.headSet(foo);
+ * ...
+ * 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, 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,2163 ****
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;
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
--- 2441,2451 ----
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
! this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
*** 2332,2342 ****
*/
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>
--- 2620,2629 ----
*** 2382,2448 ****
public K lastKey() {
synchronized (mutex) {return sm.lastKey();}
}
}
- // 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
! * immediate {@link ClassCastException}. Assuming a collection
! * contains no incorrectly typed elements prior to the time a
! * dynamically typesafe view is generated, and that all subsequent
! * access to the collection takes place through the view, it is
! * <i>guaranteed</i> that the collection cannot contain an incorrectly
! * typed element.
! *
! * <p>The generics mechanism in the language provides compile-time
! * (static) type checking, but it is possible to defeat this mechanism
! * with unchecked casts. Usually this is not a problem, as the compiler
! * issues warnings on all such unchecked operations. There are, however,
! * times when static type checking alone is not sufficient. For example,
! * suppose a collection is passed to a third-party library and it is
! * imperative that the library code not corrupt the collection by
! * inserting an element of the wrong type.
! *
! * <p>Another use of dynamically typesafe views is debugging. Suppose a
! * program fails with a {@code ClassCastException}, indicating that an
! * incorrectly typed element was put into a parameterized collection.
! * Unfortunately, the exception can occur at any time after the erroneous
! * element is inserted, so it typically provides little or no information
! * 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>();
! * }</pre>
! * may be replaced temporarily by this one:
! * <pre> {@code
! * Collection<String> c = Collections.checkedCollection(
! * new HashSet<String>(), 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.
*
! * <p>The returned collection does <i>not</i> pass the hashCode and equals
! * operations through to the backing collection, but relies on
! * {@code Object}'s {@code equals} and {@code hashCode} methods. This
! * is necessary to preserve the contracts of these operations in the case
! * that the backing collection is a set or a list.
*
! * <p>The returned collection will be serializable if the specified
! * collection is serializable.
*
! * <p>Since {@code null} is considered to be a value of any reference
! * type, the returned collection permits insertion of null elements
! * whenever the backing collection does.
*
! * @param c the collection for which a dynamically typesafe view is to be
* returned
* @param type the type of element that {@code c} is permitted to hold
* @return a dynamically typesafe view of the specified collection
* @since 1.5
*/
--- 2669,2899 ----
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>
! * SortedMap 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>
! * SortedMap m = Collections.synchronizedNavigableMap(new TreeMap());
! * SortedMap m2 = m.subMap(foo, bar);
! * ...
! * 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); } }
!
! @Override
! public Entry<K, V> higherEntry(K key) {
! synchronized (mutex) { return nm.higherEntry(key); }
! }
!
! @Override
! public K higherKey(K key) {
! synchronized (mutex) { return nm.higherKey(key); }
! }
!
! @Override
! public Entry<K, V> firstEntry() {
! synchronized (mutex) { return nm.firstEntry(); }
! }
!
! @Override
! public Entry<K, V> lastEntry() {
! synchronized (mutex) { return nm.lastEntry(); }
! }
!
! @Override
! public Entry<K, V> pollFirstEntry() {
! synchronized (mutex) {
! return nm.pollFirstEntry();
! }
! }
!
! @Override
! public Entry<K, V> pollLastEntry() {
! synchronized (mutex) {
! return nm.pollLastEntry();
! }
! }
!
! @Override
! public NavigableMap<K, V> descendingMap() {
! synchronized (mutex) {
! return (NavigableMap<K,V>)
! new SynchronizedNavigableMap(nm.descendingMap(), mutex);
! }
! }
!
! @Override
! public NavigableSet<K> navigableKeySet() {
! synchronized (mutex) {
! return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex);
! }
! }
!
! @Override
! public NavigableSet<K> descendingKeySet() {
! synchronized (mutex) {
! return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex);
! }
! }
!
! @Override
! public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
! synchronized (mutex) {
! return (NavigableMap<K,V>) new SynchronizedNavigableMap(
! nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex);
! }
! }
!
! @Override
! public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
! synchronized (mutex) {
! return (NavigableMap<K,V>) new SynchronizedNavigableMap(
! nm.headMap(toKey, inclusive), mutex);
! }
! }
!
! @Override
! public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
! synchronized (mutex) {
! return (NavigableMap<K,V>) 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
! * immediate {@link ClassCastException}. Assuming a collection
! * contains no incorrectly typed elements prior to the time a
! * dynamically typesafe view is generated, and that all subsequent
! * access to the collection takes place through the view, it is
! * <i>guaranteed</i> that the collection cannot contain an incorrectly
! * typed element.
! *
! * <p>The generics mechanism in the language provides compile-time
! * (static) type checking, but it is possible to defeat this mechanism
! * with unchecked casts. Usually this is not a problem, as the compiler
! * issues warnings on all such unchecked operations. There are, however,
! * times when static type checking alone is not sufficient. For example,
! * suppose a collection is passed to a third-party library and it is
! * imperative that the library code not corrupt the collection by
! * inserting an element of the wrong type.
! *
! * <p>Another use of dynamically typesafe views is debugging. Suppose a
! * program fails with a {@code ClassCastException}, indicating that an
! * incorrectly typed element was put into a parameterized collection.
! * Unfortunately, the exception can occur at any time after the erroneous
! * element is inserted, so it typically provides little or no information
! * 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<>();
! * }</pre>
! * may be replaced temporarily by this one:
! * <pre> {@code
! * Collection<String> c = Collections.checkedCollection(
! * 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.
! *
! * <p>The returned collection does <i>not</i> pass the hashCode and equals
! * operations through to the backing collection, but relies on
! * {@code Object}'s {@code equals} and {@code hashCode} methods. This
! * is necessary to preserve the contracts of these operations in the case
! * that the backing collection is a set or a list.
! *
! * <p>The returned collection will be serializable if the specified
! * collection is serializable.
! *
! * <p>Since {@code null} is considered to be a value of any reference
! * type, the returned collection permits insertion of null elements
! * whenever the backing collection does.
! *
! * @param c the collection for which a dynamically typesafe view is to be
* returned
* @param type the type of element that {@code c} is permitted to hold
* @return a dynamically typesafe view of the specified collection
* @since 1.5
*/
*** 2704,2713 ****
--- 3155,3165 ----
*/
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,2735 ****
--- 3178,3258 ----
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, 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,2952 ****
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;
}
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean containsKey(Object key) { return m.containsKey(key); }
--- 3461,3473 ----
return "Attempt to insert " + value.getClass() +
" value into map with value type " + valueType;
}
CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> 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); }
*** 3323,3332 ****
--- 3844,4019 ----
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 NavigableMap<K,V> subMap(K fromKey, K toKey) {
+ return checkedNavigableMap((NavigableMap<K,V>)nm.subMap(fromKey, toKey),
+ keyType, valueType);
+ }
+ public NavigableMap<K,V> headMap(K toKey) {
+ return checkedNavigableMap((NavigableMap<K,V>)nm.headMap(toKey), keyType, valueType);
+ }
+ public NavigableMap<K,V> tailMap(K fromKey) {
+ return checkedNavigableMap((NavigableMap<K,V>)nm.tailMap(fromKey), keyType, valueType);
+ }
+
+ @Override
+ public Entry<K, V> lowerEntry(K key) {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lowerEntry(key), valueType);
+ }
+
+ @Override
+ public K lowerKey(K key) {
+ return nm.lowerKey(key);
+ }
+
+ @Override
+ public Entry<K, V> floorEntry(K key) {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lowerEntry(key), valueType);
+ }
+
+ @Override
+ public K floorKey(K key) {
+ return nm.floorKey(key);
+ }
+
+ @Override
+ public Entry<K, V> ceilingEntry(K key) {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.ceilingEntry(key), valueType);
+ }
+
+ @Override
+ public K ceilingKey(K key) {
+ return nm.ceilingKey(key);
+ }
+
+ @Override
+ public Entry<K, V> higherEntry(K key) {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.higherEntry(key), valueType);
+ }
+
+ @Override
+ public K higherKey(K key) {
+ return nm.higherKey(key);
+ }
+
+ @Override
+ public Entry<K, V> firstEntry() {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.firstEntry(), valueType);
+ }
+
+ @Override
+ public Entry<K, V> lastEntry() {
+ return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lastEntry(), valueType);
+ }
+
+ @Override
+ public Entry<K, V> pollFirstEntry() {
+ Entry<K,V> entry = nm.pollFirstEntry();
+ return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType);
+ }
+
+ @Override
+ public Entry<K, V> pollLastEntry() {
+ Entry<K,V> entry = nm.pollLastEntry();
+ return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType);
+ }
+
+ @Override
+ public NavigableMap<K, V> descendingMap() {
+ return checkedNavigableMap(nm.descendingMap(), keyType, valueType);
+ }
+
+ @Override
+ public NavigableSet<K> navigableKeySet() {
+ return checkedNavigableSet(nm.navigableKeySet(), keyType);
+ }
+
+ @Override
+ public NavigableSet<K> descendingKeySet() {
+ return checkedNavigableSet(nm.descendingKeySet(), keyType);
+ }
+
+ @Override
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType);
+ }
+
+ @Override
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+ return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType);
+ }
+
+ @Override
+ 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,
*
*** 3465,3479 ****
*
* <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.)
*
* @see #EMPTY_SET
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <T> Set<T> emptySet() {
--- 4152,4167 ----
*
* <p>This example illustrates the type-safe way to obtain an empty set:
* <pre>
* Set<String> s = Collections.emptySet();
* </pre>
! * @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")
public static final <T> Set<T> emptySet() {
*** 3523,3742 ****
return EMPTY_SET;
}
}
/**
! * Returns the 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.
*
! * @since 1.8
*/
@SuppressWarnings("unchecked")
! public static final <E> SortedSet<E> emptySortedSet() {
! return (SortedSet<E>) new EmptySortedSet<>();
}
/**
* @serial include
*/
! 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(); }
}
/**
! * The empty list (immutable). This list is serializable.
*
! * @see #emptyList()
*/
! @SuppressWarnings("rawtypes")
! public static final List EMPTY_LIST = new EmptyList<>();
/**
! * Returns the 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.)
! *
! * @see #EMPTY_LIST
! * @since 1.5
*/
! @SuppressWarnings("unchecked")
! public static final <T> List<T> emptyList() {
! return (List<T>) EMPTY_LIST;
! }
!
/**
! * @serial include
*/
! private static class EmptyList<E>
! extends AbstractList<E>
! implements RandomAccess, Serializable {
! private static final long serialVersionUID = 8842843931221139166L;
! public Iterator<E> iterator() {
! return emptyIterator();
! }
! public ListIterator<E> listIterator() {
! return emptyListIterator();
! }
! 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 <T> T[] toArray(T[] a) {
! if (a.length > 0)
! a[0] = null;
! return a;
}
! public E get(int index) {
! throw new IndexOutOfBoundsException("Index: "+index);
}
! public boolean equals(Object o) {
! return (o instanceof List) && ((List<?>)o).isEmpty();
}
! public int hashCode() { return 1; }
! @Override
! public boolean removeIf(Predicate<? super E> filter) {
! Objects.requireNonNull(filter);
! return false;
}
@Override
! public void replaceAll(UnaryOperator<E> operator) {
! Objects.requireNonNull(operator);
}
! @Override
! public void sort(Comparator<? super E> c) {
! Objects.requireNonNull(c);
}
- // Override default methods in Collection
@Override
! public void forEach(Consumer<? super E> action) {
! Objects.requireNonNull(action);
}
@Override
! public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
! // Preserves singleton property
! private Object readResolve() {
! return EMPTY_LIST;
}
}
/**
* The empty map (immutable). This map is serializable.
--- 4211,4647 ----
return EMPTY_SET;
}
}
/**
! * 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> {@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 <E> SortedSet<E> emptySortedSet() {
! return (SortedSet<E>) EmptyNavigableSet.EMPTY_NAVIGABLE_SET;
! }
!
! /**
! * 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
! */
! @SuppressWarnings("unchecked")
! public static <E> NavigableSet<E> emptyNavigableSet() {
! return (NavigableSet<E>) EmptyNavigableSet.EMPTY_NAVIGABLE_SET;
! }
!
! /**
! * The empty list (immutable). This list is serializable.
! *
! * @see #emptyList()
! */
! @SuppressWarnings("rawtypes")
! public static final List EMPTY_LIST = new EmptyList<>();
!
! /**
! * Returns the 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.)
*
! * @see #EMPTY_LIST
! * @since 1.5
*/
@SuppressWarnings("unchecked")
! public static final <T> List<T> emptyList() {
! return (List<T>) EMPTY_LIST;
}
/**
* @serial include
*/
! private static class EmptyList<E>
! extends AbstractList<E>
! implements RandomAccess, Serializable {
! private static final long serialVersionUID = 8842843931221139166L;
!
! public Iterator<E> iterator() {
! return emptyIterator();
! }
! public ListIterator<E> listIterator() {
! return emptyListIterator();
! }
!
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 <T> T[] toArray(T[] a) {
if (a.length > 0)
a[0] = null;
return a;
}
! public E get(int index) {
! throw new IndexOutOfBoundsException("Index: "+index);
}
! public boolean equals(Object o) {
! return (o instanceof List) && ((List<?>)o).isEmpty();
}
! public int hashCode() { return 1; }
! @Override
! public boolean removeIf(Predicate<? super E> filter) {
! Objects.requireNonNull(filter);
! return false;
}
! @Override
! public void replaceAll(UnaryOperator<E> operator) {
! Objects.requireNonNull(operator);
! }
! @Override
! public void sort(Comparator<? super E> c) {
! Objects.requireNonNull(c);
}
! // Override default methods in Collection
! @Override
! public void forEach(Consumer<? super E> action) {
! Objects.requireNonNull(action);
}
@Override
! public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
! // Preserves singleton property
! private Object readResolve() {
! return EMPTY_LIST;
! }
}
! /**
! * An empty navigable map with enforced bounds upon the key set. The bounds
! * are generated via the various sub-map operations and enforced on
! * subsequent sub-map operations.
! *
! * @serial include
! *
! * @param <K> type of keys, if there were any, and bounds
! * @param <V> type of values, if there were any
! */
! private static class BoundedEmptyNavigableMap<K,V> extends EmptyNavigableMap<K,V>
! implements Serializable {
!
! private static final long serialVersionUID = 5065418537829651507L;
!
! /**
! * Our bounded keyset.
! */
! final BoundedEmptyNavigableSet keySet;
!
! private BoundedEmptyNavigableMap(BoundedEmptyNavigableSet keySet) {
! this.keySet = Objects.requireNonNull(keySet);
}
+ public Comparator<? super K> comparator()
+ { return keySet.comparator(); }
+
@Override
! public NavigableMap<K, V> descendingMap() {
! NavigableSet<K> descending = keySet.descendingSet();
! if(descending == Collections.emptyNavigableSet()) {
! return Collections.emptyNavigableMap();
! } else {
! return new BoundedEmptyNavigableMap((BoundedEmptyNavigableSet<K>) descending);
}
}
+ public BoundedEmptyNavigableSet<K> navigableKeySet() { return keySet; }
+ public EmptyNavigableSet<K> descendingKeySet()
+ { return keySet.descendingSet(); }
+
@Override
! public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
! return new BoundedEmptyNavigableMap(keySet.subSet(
! fromKey, fromInclusive,
! toKey, toInclusive));
}
@Override
! public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
! return new BoundedEmptyNavigableMap(
! keySet.headSet(toKey, inclusive));
}
@Override
! public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
! return new BoundedEmptyNavigableMap(
! keySet.tailSet(fromKey, inclusive));
! }
}
+ /**
+ * @serial include
+ *
+ * @param <E> type of elements, if there were any
+ */
+ private static class EmptyNavigableSet<E> extends EmptySet<E>
+ implements NavigableSet<E>, Serializable {
+ private static final long serialVersionUID = -6291252904449939134L;
+
+ @SuppressWarnings("rawtypes")
+ private static final NavigableSet EMPTY_NAVIGABLE_SET =
+ new EmptyNavigableSet();
+
+ EmptyNavigableSet() { }
+
@Override
! public boolean contains(Object obj) {
! Comparable<E> e = (Comparable<E>) Objects.requireNonNull(obj);
! return obj != e;
! }
!
! // Preserves singleton property
! private Object readResolve()
! { return Collections.emptyNavigableSet(); }
! public E lower(E e) { return null; }
! public E floor(E e) { return null; }
! public E ceiling(E e) { return null; }
! public E higher(E e) { return null; }
! public E pollFirst() { throw new UnsupportedOperationException(); }
! public E pollLast() { throw new UnsupportedOperationException(); }
!
! public EmptyNavigableSet<E> descendingSet() {
! return new BoundedEmptyNavigableSet(null, false, null, false, true);
! }
!
! public Iterator<E> descendingIterator()
! { return Collections.emptyIterator(); }
!
! public BoundedEmptyNavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
! return new BoundedEmptyNavigableSet<>(
! (Comparable<E>) Objects.requireNonNull(fromElement), fromInclusive,
! (Comparable<E>) Objects.requireNonNull(toElement), toInclusive,
! false);
! }
!
! public BoundedEmptyNavigableSet<E> headSet(E toElement, boolean inclusive) {
! return new BoundedEmptyNavigableSet<>(
! null, false,
! (Comparable<E>) Objects.requireNonNull(toElement), inclusive,
! false);
! }
!
! public BoundedEmptyNavigableSet<E> tailSet(E fromElement, boolean inclusive) {
! return new BoundedEmptyNavigableSet<>(
! (Comparable<E>) Objects.requireNonNull(fromElement), inclusive,
! null, false,
! false);
}
! public final SortedSet<E> subSet(E fromElement, E toElement)
! { return subSet(fromElement, true, toElement, false); }
! public final SortedSet<E> headSet(E toElement)
! { return headSet(toElement, true); }
! public final SortedSet<E> tailSet(E fromElement)
! { return tailSet(fromElement, false); }
!
! public Comparator<? super E> comparator() { return null; }
! public E first() { throw new NoSuchElementException(); }
! public E last() { throw new NoSuchElementException(); }
}
/**
! * An empty NavigableSet but bounds are maintained. If you try to sub-set
! * outside the bounds you will get an IllegalArgumentException.
*
! * @serial include
! *
! * @param <E> type of elements, if there were any, and bounds
*/
! private static class BoundedEmptyNavigableSet<E> extends EmptyNavigableSet<E>
! implements Serializable {
! private static final long serialVersionUID = 3393358742248855583L;
/**
! * {@code true} if lowerBound is "greater" than upperBound.
*/
! final boolean descending;
/**
! * {@code true} if lowerBound is inclusive.
*/
! final boolean lowerInclusive;
! /**
! * {@code true} if upperBound is inclusive.
! */
! final boolean upperInclusive;
! /**
! * The lower bound of the set.
! */
! final Comparable<E> lowerBound;
! /**
! * The upper bound of the set.
! */
! final Comparable<E> upperBound;
! public BoundedEmptyNavigableSet(
! Comparable<E> fromElement, boolean lowerInclusive,
! Comparable<E> toElement, boolean upperInclusive,
! boolean descending) {
! this.descending = descending;
! if ((fromElement != null) && (toElement != null)) {
! // both bounds are present we need to ensure that they make
! // sense.
! int fromCompared = Integer.signum(fromElement.compareTo((E)toElement));
! int toCompared = Integer.signum(toElement.compareTo((E)fromElement));
! if(fromCompared != -toCompared) {
! throw new IllegalArgumentException("inconsistent compareTo");
! }
! if (descending) {
! if (fromCompared < 0) {
! throw new IllegalArgumentException();
! }
! } else {
! if (fromCompared > 0) {
! throw new IllegalArgumentException();
! }
! }
! }
! this.lowerBound = fromElement;
! this.lowerInclusive = lowerInclusive;
! this.upperBound = toElement;
! this.upperInclusive = upperInclusive;
}
! @Override
! public Comparator<? super E> comparator()
! { return descending ? Collections.reverseOrder() : null; }
!
! private Comparable<E> inBounds(E element) {
! if (!descending) {
! if (null != lowerBound) {
! if (lowerInclusive) {
! if (lowerBound.compareTo(element) > 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! } else {
! if (lowerBound.compareTo(element) >= 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! }
}
! if (null != upperBound) {
! if (upperInclusive) {
! if (upperBound.compareTo(element) < 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! } else {
! if (upperBound.compareTo(element) <= 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! }
! }
! } else {
! if (null != lowerBound) {
! if (lowerInclusive) {
! if (lowerBound.compareTo(element) < 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! } else {
! if (lowerBound.compareTo(element) <= 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! }
}
! if (null != upperBound) {
! if (upperInclusive) {
! if (upperBound.compareTo(element) > 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! } else {
! if (upperBound.compareTo(element) >= 0) {
! throw new IllegalArgumentException("out of bounds");
! }
! }
! }
! }
! return (Comparable<E>) Objects.requireNonNull(element);
}
+
@Override
! public EmptyNavigableSet<E> descendingSet() {
! if (upperBound == null && lowerBound == null && descending) {
! return (EmptyNavigableSet) Collections.emptyNavigableSet();
}
! return new BoundedEmptyNavigableSet(
! upperBound, upperInclusive,
! lowerBound, lowerInclusive,
! !descending);
}
@Override
! public BoundedEmptyNavigableSet<E> subSet(E fromKey, boolean fromInclusive, E toKey, boolean toInclusive) {
! return new BoundedEmptyNavigableSet(
! inBounds(fromKey), fromInclusive,
! inBounds(toKey), toInclusive,
! descending);
}
@Override
! public BoundedEmptyNavigableSet<E> headSet(E toKey, boolean inclusive) {
! return new BoundedEmptyNavigableSet(
! lowerBound, lowerInclusive,
! inBounds(Objects.requireNonNull(toKey)), inclusive,
! descending);
! }
! @Override
! public BoundedEmptyNavigableSet<E> tailSet(E fromKey, boolean inclusive) {
! return new BoundedEmptyNavigableSet(
! inBounds(Objects.requireNonNull(fromKey)), inclusive,
! upperBound, upperInclusive,
! descending);
}
}
/**
* The empty map (immutable). This map is serializable.
*** 3748,3775 ****
public static final Map EMPTY_MAP = new EmptyMap<>();
/**
* Returns the empty map (immutable). This map is serializable.
*
! * <p>This example illustrates the type-safe way to obtain an empty set:
* <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.)
*
* @see #EMPTY_MAP
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <K,V> Map<K,V> emptyMap() {
return (Map<K,V>) EMPTY_MAP;
}
/**
* @serial include
*/
private static class EmptyMap<K,V>
extends AbstractMap<K,V>
implements Serializable
--- 4653,4719 ----
public static final Map EMPTY_MAP = new EmptyMap<>();
/**
* Returns the empty map (immutable). This map is serializable.
*
! * <p>This example illustrates the type-safe way to obtain an empty map:
* <pre>
* Map<String, Date> s = Collections.emptyMap();
* </pre>
! * @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>) EmptyNavigableMap.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>) EmptyNavigableMap.EMPTY_NAVIGABLE_MAP;
+ }
+
+ /**
* @serial include
*/
private static class EmptyMap<K,V>
extends AbstractMap<K,V>
implements Serializable
*** 3856,3865 ****
--- 4800,4932 ----
private Object readResolve() {
return EMPTY_MAP;
}
}
+ /**
+ * An empty navigable map.
+ *
+ * @param <K> type of keys, if there were any in this map
+ * @param <V> type of values, if there were any in this map
+ *
+ * @serial include
+ */
+ private static class EmptyNavigableMap<K,V> extends EmptyMap<K,V>
+ implements NavigableMap<K,V>, Serializable {
+ private static final long serialVersionUID = -2239321462712562324L;
+
+ @SuppressWarnings("rawtypes")
+ private static final NavigableMap EMPTY_NAVIGABLE_MAP =
+ new EmptyNavigableMap();
+
+ EmptyNavigableMap() {}
+
+ @Override
+ public boolean containsKey(Object obj) {
+ Comparable<K> e = (Comparable<K>) Objects.requireNonNull(obj);
+ return obj != e;
+ }
+
+ // Preserves singleton property
+ private Object readResolve() { return Collections.emptyNavigableMap();}
+
+ public final K firstKey() { throw new NoSuchElementException(); }
+ public final K lastKey() { throw new NoSuchElementException(); }
+
+ public Entry<K, V> lowerEntry(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public K lowerKey(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public Entry<K, V> floorEntry(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public K floorKey(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public Entry<K, V> ceilingEntry(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public K ceilingKey(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public Entry<K, V> higherEntry(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public K higherKey(K key) {
+ Comparable<K> k = (Comparable<K>) Objects.requireNonNull(key);
+ return null;
+ }
+
+ public Entry<K, V> firstEntry() { return null; }
+ public Entry<K, V> lastEntry() { return null; }
+ public Entry<K, V> pollFirstEntry()
+ { throw new UnsupportedOperationException(); }
+ public Entry<K, V> pollLastEntry()
+ { throw new UnsupportedOperationException(); }
+ public NavigableMap<K, V> descendingMap() {
+ EmptyNavigableSet<K> descendingKeys = descendingKeySet();
+
+ if(descendingKeys == emptyNavigableSet()) {
+ return emptyNavigableMap();
+ } else {
+ return new BoundedEmptyNavigableMap<>((BoundedEmptyNavigableSet<K>) descendingKeys);
+ }
+ }
+
+ public EmptyNavigableSet<K> navigableKeySet()
+ { return (EmptyNavigableSet<K>) Collections.emptyNavigableSet(); }
+ public EmptyNavigableSet<K> descendingKeySet() {
+ return navigableKeySet().descendingSet();
+ }
+
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+ return new BoundedEmptyNavigableMap<>(
+ ((EmptyNavigableSet<K>) Collections.emptyNavigableSet()).subSet(
+ fromKey, fromInclusive,
+ toKey, toInclusive));
+ }
+
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+ return new BoundedEmptyNavigableMap<>(
+ ((EmptyNavigableSet<K>) Collections.emptyNavigableSet())
+ .headSet(toKey, inclusive));
+ }
+
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+ return new BoundedEmptyNavigableMap<>(
+ ((EmptyNavigableSet<K>) Collections.emptyNavigableSet())
+ .tailSet(fromKey, inclusive));
+ }
+
+ public final SortedMap<K, V> subMap(K fromKey, K toKey)
+ { return subMap(fromKey, true, toKey, false); }
+
+ public final SortedMap<K, V> headMap(K toKey)
+ { return headMap(toKey, true); }
+
+ public final SortedMap<K, V> tailMap(K fromKey)
+ { return tailMap(fromKey, false); }
+
+ public Comparator<? super K> comparator() { return null; }
+ }
+
// Singleton collections
/**
* Returns an immutable set containing only the specified object.
* The returned set is serializable.
*** 4070,4086 ****
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;
--- 5137,5149 ----
*** 4659,4668 ****
--- 5722,5847 ----
s = m.keySet();
}
}
/**
+ * Returns a navigable set backed by the specified map. The resulting
+ * navigable set displays the same ordering, concurrency, and performance
+ * characteristics as the backing map. In essence, this factory method
+ * provides a {@link NavigableSet} implementation corresponding to any
+ * {@link NavigableMap} implementation. There is no need to use this method
+ * on a {@link NavigableMap} implementation that already has a corresponding
+ * {@link Set} implementation (such as {@link TreeMap}).
+ *
+ * <p>Each method invocation on the set returned by this method results in
+ * exactly one method invocation on the backing map or its {@code keySet}
+ * view, with one exception. The {@code addAll} method is implemented
+ * as a sequence of {@code put} invocations on the backing map.
+ *
+ * <p>The specified map must be empty at the time this method is invoked,
+ * and should not be accessed directly after this method returns. These
+ * conditions are ensured if the map is created empty, passed directly
+ * to this method, and no reference to the map is retained, as illustrated
+ * in the following code fragment:
+ * <pre> {@code
+ * Set<Object> weakHashSet = Collections.newNavigableSetFromNavigableMap(
+ * new WeakHashMap<Object, Boolean>());
+ * }</pre>
+ *
+ * @param map the backing navigable map
+ * @return the navigable set backed by the map
+ * @throws IllegalArgumentException if {@code map} is not empty
+ * @since 1.8
+ */
+ public static <E> NavigableSet<E> newNavigableSetFromNavigableMap(NavigableMap<E, Boolean> map) {
+ return new NavigableSetFromNavigableMap<>(map);
+ }
+
+ /**
+ * @serial include
+ */
+ private static class NavigableSetFromNavigableMap<E> extends AbstractSet<E>
+ implements NavigableSet<E>, Serializable
+ {
+ private static final long serialVersionUID = -7303807726339382839L;
+
+ private final NavigableMap<E, Boolean> m; // The backing map
+ private transient NavigableSet<E> s; // Its keySet
+
+ NavigableSetFromNavigableMap(NavigableMap<E, Boolean> map) {
+ if (!map.isEmpty())
+ throw new IllegalArgumentException("Map is non-empty");
+ m = map;
+ s = map.navigableKeySet();
+ }
+
+ public void clear() { m.clear(); }
+ public int size() { return m.size(); }
+ public boolean isEmpty() { return m.isEmpty(); }
+ public boolean contains(Object o) { return m.containsKey(o); }
+ public boolean remove(Object o) { return m.remove(o) != null; }
+ public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
+ public Iterator<E> iterator() { return s.iterator(); }
+ public Object[] toArray() { return s.toArray(); }
+ public <T> T[] toArray(T[] a) { return s.toArray(a); }
+ public String toString() { return s.toString(); }
+ public int hashCode() { return s.hashCode(); }
+ public boolean equals(Object o) { return o == this || s.equals(o); }
+ public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
+ public boolean removeAll(Collection<?> c) {return s.removeAll(c);}
+ public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
+ // addAll is the only inherited implementation
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) { s.forEach(action); }
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ return s.removeIf(filter);
+ }
+
+ @Override
+ public Spliterator<E> spliterator() {return s.spliterator();}
+
+ private void readObject(java.io.ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+ if (null == m) {
+ throw new InvalidObjectException("null map");
+ }
+ s = m.navigableKeySet();
+ }
+
+ public E lower(E e) { return m.lowerKey(e); }
+ public E floor(E e) { return m.floorKey(e); }
+ public E ceiling(E e) { return m.ceilingKey(e); }
+ public E higher(E e) { return m.higherKey(e); }
+ public E pollFirst() { return s.pollFirst(); }
+ public E pollLast() { return s.pollLast(); }
+ public NavigableSet<E> descendingSet() { return s.descendingSet(); }
+ public Iterator<E> descendingIterator(){return s.descendingIterator();}
+
+ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+ return s.subSet(fromElement, fromInclusive, toElement, toInclusive);
+ }
+
+ public NavigableSet<E> headSet(E toElement, boolean inclusive)
+ { return s.headSet(toElement, inclusive); }
+ public NavigableSet<E> tailSet(E fromElement, boolean inclusive)
+ { return s.tailSet(fromElement, inclusive); }
+ public SortedSet<E> subSet(E fromElement, E toElement)
+ { return s.subSet(fromElement, toElement); }
+ public SortedSet<E> headSet(E toElement)
+ { return s.headSet(toElement); }
+ public SortedSet<E> tailSet(E fromElement)
+ { return s.tailSet(fromElement); }
+ public Comparator<? super E> comparator() { return s.comparator(); }
+ public E first() { return s.first(); }
+ public E last() { return s.last(); }
+ }
+
+ /**
* Returns a view of a {@link Deque} as a Last-in-first-out (Lifo)
* {@link Queue}. Method <tt>add</tt> is mapped to <tt>push</tt>,
* <tt>remove</tt> is mapped to <tt>pop</tt> and so on. This
* view can be useful when you would like to use a method
* requiring a <tt>Queue</tt> but you need Lifo ordering.