# HG changeset patch # User mduigou # Date 1370629512 25200 # Node ID 86df96c425eb045ffd8644d81f01df5c1a5964cc # Parent f5f54e493a645822aa5fb7a13e15064103f8c5ea 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set} Reviewed-by: dmocek diff --git a/src/share/classes/java/util/Collections.java b/src/share/classes/java/util/Collections.java --- a/src/share/classes/java/util/Collections.java +++ b/src/share/classes/java/util/Collections.java @@ -27,6 +27,7 @@ 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; @@ -136,7 +137,7 @@ * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). 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. @@ -197,7 +198,7 @@ * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). 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. @@ -1212,6 +1213,72 @@ } /** + * 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 + * subSet, headSet, or tailSet views, result in + * an UnsupportedOperationException.

+ * + * 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 NavigableSet unmodifiableNavigableSet(NavigableSet s) { + return new UnmodifiableNavigableSet<>(s); + } + + /** + * @serial include + */ + static class UnmodifiableNavigableSet + extends UnmodifiableSortedSet + implements NavigableSet, Serializable { + + private static final long serialVersionUID = -6027448201786391929L; + + private final NavigableSet ns; + + UnmodifiableNavigableSet(NavigableSet 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 descendingSet() { + return new UnmodifiableNavigableSet<>(ns.descendingSet()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new UnmodifiableNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>(ns.headSet(toElement, inclusive)); + } + + @Override + public NavigableSet 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 @@ -1238,6 +1305,7 @@ static class UnmodifiableList extends UnmodifiableCollection implements List { private static final long serialVersionUID = -283967356065247728L; + final List list; UnmodifiableList(List list) { @@ -1670,6 +1738,131 @@ 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 + * subMap, headMap, or tailMap views, result in + * an UnsupportedOperationException.

+ * + * 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 NavigableMap unmodifiableNavigableMap(NavigableMap m) { + return new UnmodifiableNavigableMap<>(m); + } + + /** + * @serial include + */ + static class UnmodifiableNavigableMap + extends UnmodifiableSortedMap + implements NavigableMap, Serializable { + private static final long serialVersionUID = -4858195264774772197L; + + private final NavigableMap nm; + + UnmodifiableNavigableMap(NavigableMap m) {super(m); nm = m;} + + @Override + public Entry lowerEntry(K key) { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lowerEntry(key)); + } + + @Override + public K lowerKey(K key) { + return nm.lowerKey(key); + } + + @Override + public Entry floorEntry(K key) { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.floorEntry(key)); + } + + @Override + public K floorKey(K key) { + return nm.floorKey(key); + } + + @Override + public Entry ceilingEntry(K key) { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.ceilingEntry(key)); + } + + @Override + public K ceilingKey(K key) { + return nm.ceilingKey(key); + } + + @Override + public Entry higherEntry(K key) { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.higherEntry(key)); + } + + @Override + public K higherKey(K key) { + return nm.higherKey(key); + } + + @Override + public Entry firstEntry() { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.firstEntry()); + } + + @Override + public Entry lastEntry() { + return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lastEntry()); + } + + @Override + public Entry pollFirstEntry() { + Entry entry = (Entry) nm.pollFirstEntry(); + return (null == entry) ? null : new UnmodifiableEntrySet.UnmodifiableEntry(entry); + } + + @Override + public Entry pollLastEntry() { + Entry entry = (Entry) nm.pollLastEntry(); + return (null == entry) ? null : new UnmodifiableEntrySet.UnmodifiableEntry(entry); + } + + @Override + public NavigableMap descendingMap() { + return unmodifiableNavigableMap(nm.descendingMap()); + } + + @Override + public NavigableSet navigableKeySet() { + return unmodifiableNavigableSet(nm.navigableKeySet()); + } + + @Override + public NavigableSet descendingKeySet() { + return unmodifiableNavigableSet(nm.descendingKeySet()); + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return unmodifiableNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + return unmodifiableNavigableMap(nm.headMap(toKey, inclusive)); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive)); + } + } // Synch Wrappers @@ -1723,14 +1916,13 @@ final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection c) { - if (c==null) - throw new NullPointerException(); - this.c = c; + this.c = Objects.requireNonNull(c); mutex = this; } + SynchronizedCollection(Collection c, Object mutex) { - this.c = c; - this.mutex = mutex; + this.c = Objects.requireNonNull(c); + this.mutex = Objects.requireNonNull(mutex); } public int size() { @@ -1945,6 +2137,104 @@ } /** + * Returns a synchronized (thread-safe) sorted set backed by the specified + * navigable set. In order to guarantee serial access, it is critical that + * all access to the backing sorted set is accomplished + * through the returned navigable set (or its views).

+ * + * 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. + *

+     *  SortedSet s = Collections.synchronizedNavigableSet(new TreeSet());
+     *      ...
+     *  synchronized (s) {
+     *      Iterator i = s.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * or: + *
+     *  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());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

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 NavigableSet synchronizedNavigableSet(NavigableSet s) { + return new SynchronizedNavigableSet<>(s); + } + + /** + * @serial include + */ + static class SynchronizedNavigableSet + extends SynchronizedSortedSet + implements NavigableSet + { + private static final long serialVersionUID = -5505529816273629798L; + + private final NavigableSet ns; + + SynchronizedNavigableSet(NavigableSet s) { + super(s); + ns = s; + } + + SynchronizedNavigableSet(NavigableSet 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 descendingSet() { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex); + } + } + + public Iterator descendingIterator() + { synchronized (mutex) { return descendingSet().iterator(); } } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex); + } + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex); + } + } + + public NavigableSet 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 * all access to the backing list is accomplished @@ -2153,9 +2443,7 @@ final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { - if (m==null) - throw new NullPointerException(); - this.m = m; + this.m = Objects.requireNonNull(m); mutex = this; } @@ -2334,7 +2622,6 @@ return new SynchronizedSortedMap<>(m); } - /** * @serial include */ @@ -2384,6 +2671,170 @@ } } + /** + * Returns a synchronized (thread-safe) navigable map backed by the + * specified navigable map. In order to guarantee serial access, it is + * critical that all access to the backing navigable map is + * accomplished through the returned navigable map (or its views).

+ * + * 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. + *

+     *  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());
+     *  }
+     * 
+ * or: + *
+     *  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());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

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 NavigableMap synchronizedNavigableMap(NavigableMap m) { + return new SynchronizedNavigableMap<>(m); + } + + /** + * A synchronized NavigableMap. + * + * @serial include + */ + static class SynchronizedNavigableMap + extends SynchronizedSortedMap + implements NavigableMap + { + private static final long serialVersionUID = 699392247599746807L; + + private final NavigableMap nm; + + SynchronizedNavigableMap(NavigableMap m) { + super(m); + nm = m; + } + SynchronizedNavigableMap(NavigableMap m, Object mutex) { + super(m, mutex); + nm = m; + } + + public Entry lowerEntry(K key) + { synchronized (mutex) { return nm.lowerEntry(key); } } + public K lowerKey(K key) + { synchronized (mutex) { return nm.lowerKey(key); } } + public Entry floorEntry(K key) + { synchronized (mutex) { return nm.floorEntry(key); } } + public K floorKey(K key) + { synchronized (mutex) { return nm.floorKey(key); } } + public Entry ceilingEntry(K key) + { synchronized (mutex) { return nm.ceilingEntry(key); } } + public K ceilingKey(K key) + { synchronized (mutex) { return nm.ceilingKey(key); } } + + @Override + public Entry higherEntry(K key) { + synchronized (mutex) { return nm.higherEntry(key); } + } + + @Override + public K higherKey(K key) { + synchronized (mutex) { return nm.higherKey(key); } + } + + @Override + public Entry firstEntry() { + synchronized (mutex) { return nm.firstEntry(); } + } + + @Override + public Entry lastEntry() { + synchronized (mutex) { return nm.lastEntry(); } + } + + @Override + public Entry pollFirstEntry() { + synchronized (mutex) { + return nm.pollFirstEntry(); + } + } + + @Override + public Entry pollLastEntry() { + synchronized (mutex) { + return nm.pollLastEntry(); + } + } + + @Override + public NavigableMap descendingMap() { + synchronized (mutex) { + return (NavigableMap) + new SynchronizedNavigableMap(nm.descendingMap(), mutex); + } + } + + @Override + public NavigableSet navigableKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex); + } + } + + @Override + public NavigableSet descendingKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex); + } + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized (mutex) { + return (NavigableMap) new SynchronizedNavigableMap( + nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); + } + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return (NavigableMap) new SynchronizedNavigableMap( + nm.headMap(toKey, inclusive), mutex); + } + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized (mutex) { + return (NavigableMap) new SynchronizedNavigableMap( + nm.tailMap(fromKey, inclusive), mutex); + } + } + } + // Dynamically typesafe collection wrappers /** @@ -2415,12 +2866,12 @@ * program to wrap the collection with a dynamically typesafe view. * For example, this declaration: *

 {@code
-     *     Collection c = new HashSet();
+     *     Collection c = new HashSet<>();
      * }
* may be replaced temporarily by this one: *
 {@code
      *     Collection c = Collections.checkedCollection(
-     *         new HashSet(), String.class);
+     *         new HashSet<>(), String.class);
      * }
* Running the program again will cause it to fail at the point where * an incorrectly typed element is inserted into the collection, clearly @@ -2706,6 +3157,7 @@ implements SortedSet, Serializable { private static final long serialVersionUID = 1599911165492914959L; + private final SortedSet ss; CheckedSortedSet(SortedSet s, Class type) { @@ -2728,6 +3180,77 @@ } } +/** + * 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 + * guaranteed that the navigable set cannot contain an incorrectly + * typed element. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + *

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 NavigableSet checkedNavigableSet(NavigableSet s, + Class type) { + return new CheckedNavigableSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedNavigableSet extends CheckedSortedSet + implements NavigableSet, Serializable + { + private static final long serialVersionUID = -5429120189805438922L; + + private final NavigableSet ns; + + CheckedNavigableSet(NavigableSet s, Class 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 descendingSet() + { return checkedNavigableSet(ns.descendingSet(), type); } + public Iterator descendingIterator() + {return checkedNavigableSet(ns.descendingSet(), type).iterator(); } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return checkedNavigableSet(ns.headSet(toElement, inclusive), type); + } + + public NavigableSet 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 @@ -2940,11 +3463,9 @@ } CheckedMap(Map m, Class keyType, Class 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(); } @@ -3325,6 +3846,172 @@ } } + /** + * 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. + * + *

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 guaranteed that the + * map cannot contain an incorrectly typed key or value. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned map will be serializable if the specified map is + * serializable. + * + *

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 type of map keys + * @param 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 NavigableMap checkedNavigableMap(NavigableMap m, + Class keyType, + Class valueType) { + return new CheckedNavigableMap<>(m, keyType, valueType); + } + + /** + * @serial include + */ + static class CheckedNavigableMap extends CheckedSortedMap + implements NavigableMap, Serializable + { + private static final long serialVersionUID = -4852462692372534096L; + + private final NavigableMap nm; + + CheckedNavigableMap(NavigableMap m, + Class keyType, Class valueType) { + super(m, keyType, valueType); + nm = m; + } + + public Comparator comparator() { return nm.comparator(); } + public K firstKey() { return nm.firstKey(); } + public K lastKey() { return nm.lastKey(); } + + public NavigableMap subMap(K fromKey, K toKey) { + return checkedNavigableMap((NavigableMap)nm.subMap(fromKey, toKey), + keyType, valueType); + } + public NavigableMap headMap(K toKey) { + return checkedNavigableMap((NavigableMap)nm.headMap(toKey), keyType, valueType); + } + public NavigableMap tailMap(K fromKey) { + return checkedNavigableMap((NavigableMap)nm.tailMap(fromKey), keyType, valueType); + } + + @Override + public Entry 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 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 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 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 firstEntry() { + return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.firstEntry(), valueType); + } + + @Override + public Entry lastEntry() { + return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lastEntry(), valueType); + } + + @Override + public Entry pollFirstEntry() { + Entry entry = nm.pollFirstEntry(); + return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + @Override + public Entry pollLastEntry() { + Entry entry = nm.pollLastEntry(); + return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + @Override + public NavigableMap descendingMap() { + return checkedNavigableMap(nm.descendingMap(), keyType, valueType); + } + + @Override + public NavigableSet navigableKeySet() { + return checkedNavigableSet(nm.navigableKeySet(), keyType); + } + + @Override + public NavigableSet descendingKeySet() { + return checkedNavigableSet(nm.descendingKeySet(), keyType); + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType); + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return checkedNavigableMap(nm.tailMap(fromKey, inclusive), keyType, valueType); + } + } + // Empty collections /** @@ -3467,11 +4154,12 @@ *

      *     Set<String> s = Collections.emptySet();
      * 
- * Implementation note: Implementations of this method need not - * create a separate 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.) - * + * @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 */ @@ -3525,121 +4213,45 @@ } /** - * Returns the empty sorted set (immutable). This set is serializable. - * - *

This example illustrates the type-safe way to obtain an empty sorted - * set: - *

-     *     SortedSet<String> s = Collections.emptySortedSet();
-     * 
- * Implementation note: Implementations of this method need not - * create a separate SortedSet object for each call. - * + * Returns an empty sorted set (immutable). This set is serializable. + * + *

This example illustrates the type-safe way to obtain an empty + * sorted set: + *

 {@code
+     *     SortedSet s = Collections.emptySortedSet();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code SortedSet} object for each call. + * + * @param type of elements, if there were any, in the set + * @return the empty sorted set * @since 1.8 */ @SuppressWarnings("unchecked") - public static final SortedSet emptySortedSet() { - return (SortedSet) new EmptySortedSet<>(); + public static SortedSet emptySortedSet() { + return (SortedSet) EmptyNavigableSet.EMPTY_NAVIGABLE_SET; } /** - * @serial include + * Returns an empty navigable set (immutable). This set is serializable. + * + *

This example illustrates the type-safe way to obtain an empty + * navigable set: + *

 {@code
+     *     NavigableSet s = Collections.emptyNavigableSet();
+     * }
+ * + * @implNote Implementations of this method need not + * create a separate {@code NavigableSet} object for each call. + * + * @param type of elements, if there were any, in the set + * @return the empty navigable set + * @since 1.8 */ - private static class EmptySortedSet - extends AbstractSet - implements SortedSet, Serializable - { - private static final long serialVersionUID = 6316515401502265487L; - public Iterator 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[] toArray(E[] a) { - if (a.length > 0) - a[0] = null; - return a; - } - - // Preserves singleton property - private Object readResolve() { - return new EmptySortedSet<>(); - } - - @Override - public Comparator comparator() { - return null; - } - - @Override - @SuppressWarnings("unchecked") - public SortedSet 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 headSet(Object toElement) { - Objects.requireNonNull(toElement); - - if (!(toElement instanceof Comparable)) { - throw new ClassCastException(); - } - - return emptySortedSet(); - } - - @Override - public SortedSet 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 action) { - Objects.requireNonNull(action); - } - - @Override - public boolean removeIf(Predicate filter) { - Objects.requireNonNull(filter); - return false; - } - - @Override - public Spliterator spliterator() { return Spliterators.emptySpliterator(); } + @SuppressWarnings("unchecked") + public static NavigableSet emptyNavigableSet() { + return (NavigableSet) EmptyNavigableSet.EMPTY_NAVIGABLE_SET; } /** @@ -3739,6 +4351,299 @@ } /** + * 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 type of keys, if there were any, and bounds + * @param type of values, if there were any + */ + private static class BoundedEmptyNavigableMap extends EmptyNavigableMap + 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 comparator() + { return keySet.comparator(); } + + @Override + public NavigableMap descendingMap() { + NavigableSet descending = keySet.descendingSet(); + + if(descending == Collections.emptyNavigableSet()) { + return Collections.emptyNavigableMap(); + } else { + return new BoundedEmptyNavigableMap((BoundedEmptyNavigableSet) descending); + } + } + + public BoundedEmptyNavigableSet navigableKeySet() { return keySet; } + public EmptyNavigableSet descendingKeySet() + { return keySet.descendingSet(); } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new BoundedEmptyNavigableMap(keySet.subSet( + fromKey, fromInclusive, + toKey, toInclusive)); + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + return new BoundedEmptyNavigableMap( + keySet.headSet(toKey, inclusive)); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return new BoundedEmptyNavigableMap( + keySet.tailSet(fromKey, inclusive)); + } + } + + /** + * @serial include + * + * @param type of elements, if there were any + */ + private static class EmptyNavigableSet extends EmptySet + implements NavigableSet, 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 = (Comparable) 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 descendingSet() { + return new BoundedEmptyNavigableSet(null, false, null, false, true); + } + + public Iterator descendingIterator() + { return Collections.emptyIterator(); } + + public BoundedEmptyNavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new BoundedEmptyNavigableSet<>( + (Comparable) Objects.requireNonNull(fromElement), fromInclusive, + (Comparable) Objects.requireNonNull(toElement), toInclusive, + false); + } + + public BoundedEmptyNavigableSet headSet(E toElement, boolean inclusive) { + return new BoundedEmptyNavigableSet<>( + null, false, + (Comparable) Objects.requireNonNull(toElement), inclusive, + false); + } + + public BoundedEmptyNavigableSet tailSet(E fromElement, boolean inclusive) { + return new BoundedEmptyNavigableSet<>( + (Comparable) Objects.requireNonNull(fromElement), inclusive, + null, false, + false); + } + + public final SortedSet subSet(E fromElement, E toElement) + { return subSet(fromElement, true, toElement, false); } + public final SortedSet headSet(E toElement) + { return headSet(toElement, true); } + public final SortedSet tailSet(E fromElement) + { return tailSet(fromElement, false); } + + public Comparator 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 type of elements, if there were any, and bounds + */ + private static class BoundedEmptyNavigableSet extends EmptyNavigableSet + 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 lowerBound; + /** + * The upper bound of the set. + */ + final Comparable upperBound; + + public BoundedEmptyNavigableSet( + Comparable fromElement, boolean lowerInclusive, + Comparable 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 comparator() + { return descending ? Collections.reverseOrder() : null; } + + private Comparable 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) Objects.requireNonNull(element); + } + + @Override + public EmptyNavigableSet descendingSet() { + if (upperBound == null && lowerBound == null && descending) { + return (EmptyNavigableSet) Collections.emptyNavigableSet(); + } + return new BoundedEmptyNavigableSet( + upperBound, upperInclusive, + lowerBound, lowerInclusive, + !descending); + } + + @Override + public BoundedEmptyNavigableSet subSet(E fromKey, boolean fromInclusive, E toKey, boolean toInclusive) { + return new BoundedEmptyNavigableSet( + inBounds(fromKey), fromInclusive, + inBounds(toKey), toInclusive, + descending); + } + + @Override + public BoundedEmptyNavigableSet headSet(E toKey, boolean inclusive) { + return new BoundedEmptyNavigableSet( + lowerBound, lowerInclusive, + inBounds(Objects.requireNonNull(toKey)), inclusive, + descending); + } + + @Override + public BoundedEmptyNavigableSet tailSet(E fromKey, boolean inclusive) { + return new BoundedEmptyNavigableSet( + inBounds(Objects.requireNonNull(fromKey)), inclusive, + upperBound, upperInclusive, + descending); + } + } + + /** * The empty map (immutable). This map is serializable. * * @see #emptyMap() @@ -3750,15 +4655,16 @@ /** * Returns the empty map (immutable). This map is serializable. * - *

This example illustrates the type-safe way to obtain an empty set: + *

This example illustrates the type-safe way to obtain an empty map: *

      *     Map<String, Date> s = Collections.emptyMap();
      * 
- * Implementation note: Implementations of this method need not - * create a separate 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.) - * + * @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 */ @@ -3768,6 +4674,44 @@ } /** + * Returns an empty sorted map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty map: + *

 {@code
+     *     SortedMap s = Collections.emptySortedMap();
+     * }
+ * + * @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 SortedMap emptySortedMap() { + return (SortedMap) EmptyNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** + * Returns an empty navigable map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty map: + *

 {@code
+     *     NavigableMap s = Collections.emptyNavigableMap();
+     * }
+ * + * @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 NavigableMap emptyNavigableMap() { + return (NavigableMap) EmptyNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** * @serial include */ private static class EmptyMap @@ -3858,6 +4802,129 @@ } } + /** + * An empty navigable map. + * + * @param type of keys, if there were any in this map + * @param type of values, if there were any in this map + * + * @serial include + */ + private static class EmptyNavigableMap extends EmptyMap + implements NavigableMap, 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 e = (Comparable) 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 lowerEntry(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public K lowerKey(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public Entry floorEntry(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public K floorKey(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public Entry ceilingEntry(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public K ceilingKey(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public Entry higherEntry(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public K higherKey(K key) { + Comparable k = (Comparable) Objects.requireNonNull(key); + return null; + } + + public Entry firstEntry() { return null; } + public Entry lastEntry() { return null; } + public Entry pollFirstEntry() + { throw new UnsupportedOperationException(); } + public Entry pollLastEntry() + { throw new UnsupportedOperationException(); } + public NavigableMap descendingMap() { + EmptyNavigableSet descendingKeys = descendingKeySet(); + + if(descendingKeys == emptyNavigableSet()) { + return emptyNavigableMap(); + } else { + return new BoundedEmptyNavigableMap<>((BoundedEmptyNavigableSet) descendingKeys); + } + } + + public EmptyNavigableSet navigableKeySet() + { return (EmptyNavigableSet) Collections.emptyNavigableSet(); } + public EmptyNavigableSet descendingKeySet() { + return navigableKeySet().descendingSet(); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet) Collections.emptyNavigableSet()).subSet( + fromKey, fromInclusive, + toKey, toInclusive)); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet) Collections.emptyNavigableSet()) + .headSet(toKey, inclusive)); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet) Collections.emptyNavigableSet()) + .tailSet(fromKey, inclusive)); + } + + public final SortedMap subMap(K fromKey, K toKey) + { return subMap(fromKey, true, toKey, false); } + + public final SortedMap headMap(K toKey) + { return headMap(toKey, true); } + + public final SortedMap tailMap(K fromKey) + { return tailMap(fromKey, false); } + + public Comparator comparator() { return null; } + } + // Singleton collections /** @@ -4071,15 +5138,11 @@ 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);} + 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 keySet = null; private transient Set> entrySet = null; @@ -4661,6 +5724,122 @@ } /** + * 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}). + * + *

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. + * + *

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: + *

 {@code
+     *    Set weakHashSet = Collections.newNavigableSetFromNavigableMap(
+     *        new WeakHashMap());
+     * }
+     *
+     * @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  NavigableSet newNavigableSetFromNavigableMap(NavigableMap map) {
+        return new NavigableSetFromNavigableMap<>(map);
+    }
+
+    /**
+     * @serial include
+     */
+    private static class NavigableSetFromNavigableMap extends AbstractSet
+        implements NavigableSet, Serializable
+    {
+        private static final long serialVersionUID = -7303807726339382839L;
+
+        private final NavigableMap m;  // The backing map
+        private transient NavigableSet s;       // Its keySet
+
+        NavigableSetFromNavigableMap(NavigableMap 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 iterator()     { return s.iterator(); }
+        public Object[] toArray()         { return s.toArray(); }
+        public  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 action)  { s.forEach(action); }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            return s.removeIf(filter);
+        }
+
+        @Override
+        public Spliterator 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 descendingSet()    { return s.descendingSet(); }
+        public Iterator descendingIterator(){return s.descendingIterator();}
+
+        public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+            return s.subSet(fromElement, fromInclusive, toElement, toInclusive);
+        }
+
+        public NavigableSet headSet(E toElement, boolean inclusive)
+                                    { return s.headSet(toElement, inclusive); }
+        public NavigableSet tailSet(E fromElement, boolean inclusive)
+                                  { return s.tailSet(fromElement, inclusive); }
+        public SortedSet subSet(E fromElement, E toElement)
+                                   { return s.subSet(fromElement, toElement); }
+        public SortedSet headSet(E toElement)
+                                               { return s.headSet(toElement); }
+        public SortedSet tailSet(E fromElement)
+                                             { return s.tailSet(fromElement); }
+        public Comparator 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 add is mapped to push,
      * remove is mapped to pop and so on. This
diff --git a/test/java/util/Collections/CheckedMapBash.java b/test/java/util/Collections/CheckedMapBash.java
--- a/test/java/util/Collections/CheckedMapBash.java
+++ b/test/java/util/Collections/CheckedMapBash.java
@@ -23,76 +23,83 @@
 
 /*
  * @test
- * @bug     4904067 5023830
+ * @bug     4904067 5023830 7129185
  * @summary Unit test for Collections.checkedMap
  * @author  Josh Bloch
+ * @run testng CheckedMapBash
  */
 
 import java.util.*;
+import java.util.function.Supplier;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertTrue;
 
 public class CheckedMapBash {
-    static Random rnd = new Random();
-    static Object nil = new Integer(0);
+    static final Random rnd = new Random();
+    static final Object nil = new Integer(0);
+    static final int numItr = 100;
+    static final int mapSize = 100;
 
-    public static void main(String[] args) {
-        int numItr = 100;
-        int mapSize = 100;
+    @Test(dataProvider = "Bash.Supplier>")
+    public static void testCheckeMap(String description, Supplier> supplier) {
+        Map m = supplier.get();
+        Object head = nil;
 
-        // Linked List test
-        for (int i=0; i> supplier) {
+        Map m = supplier.get();
         for (int i=0; i bashNavigableMapProvider() {
+        ArrayList iters = new ArrayList<>(makeCheckedMaps());
+        iters.ensureCapacity(numItr * iters.size());
+        for(int each=1; each < numItr; each++) {
+            iters.addAll( makeCheckedMaps());
+        }
+        return iters.iterator();
     }
 
-    static void fail(String s) {
-        throw new RuntimeException(s);
+    @DataProvider(name = "Supplier>", parallel = true)
+    public static Iterator navigableMapProvider() {
+        return makeCheckedMaps().iterator();
+    }
+
+    public static Collection makeCheckedMaps() {
+        return Arrays.asList(
+            new Object[]{"Collections.checkedMap(HashMap)",
+                (Supplier) () -> {return Collections.checkedMap(new HashMap(), Integer.class, Integer.class);}},
+            new Object[]{"Collections.checkedMap(TreeSet(reverseOrder)",
+                (Supplier) () -> {return Collections.checkedMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}},
+            new Object[]{"Collections.checkedMap(TreeSet).descendingSet()",
+                (Supplier) () -> {return Collections.checkedMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}},
+            new Object[]{"Collections.checkedNavigableMap(TreeSet)",
+                (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class);}},
+            new Object[]{"Collections.checkedNavigableMap(TreeSet(reverseOrder)",
+                (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}},
+            new Object[]{"Collections.checkedNavigableMap().descendingSet()",
+                (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}}
+            );
     }
 }
diff --git a/test/java/util/Collections/CheckedSetBash.java b/test/java/util/Collections/CheckedSetBash.java
--- a/test/java/util/Collections/CheckedSetBash.java
+++ b/test/java/util/Collections/CheckedSetBash.java
@@ -23,82 +23,93 @@
 
 /*
  * @test
- * @bug     4904067
+ * @bug     4904067 7129185
  * @summary Unit test for Collections.checkedSet
  * @author  Josh Bloch
+ * @run testng CheckedSetBash
  */
 
 import java.util.*;
+import java.util.function.Supplier;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertTrue;
 
 public class CheckedSetBash {
-    static Random rnd = new Random();
+    static final int numItr = 100;
+    static final int setSize = 100;
+    static final Random rnd = new Random();
 
-    public static void main(String[] args) {
-        int numItr = 100;
-        int setSize = 100;
+    @Test(dataProvider = "Supplier>")
+    public static void testCheckedSet(String description, Supplier> supplier) {
 
-        for (int i=0; i s1 = supplier.get();
+        assertTrue(s1.isEmpty());
 
-            Set s2 = newSet();
-            AddRandoms(s2, setSize);
+        AddRandoms(s1, setSize);
 
-            Set intersection = clone(s1);
-            intersection.retainAll(s2);
-            Set diff1 = clone(s1); diff1.removeAll(s2);
-            Set diff2 = clone(s2); diff2.removeAll(s1);
-            Set union = clone(s1); union.addAll(s2);
+        Set s2 = supplier.get();
 
-            if (diff1.removeAll(diff2))
-                fail("Set algebra identity 2 failed");
-            if (diff1.removeAll(intersection))
-                fail("Set algebra identity 3 failed");
-            if (diff2.removeAll(diff1))
-                fail("Set algebra identity 4 failed");
-            if (diff2.removeAll(intersection))
-                fail("Set algebra identity 5 failed");
-            if (intersection.removeAll(diff1))
-                fail("Set algebra identity 6 failed");
-            if (intersection.removeAll(diff1))
-                fail("Set algebra identity 7 failed");
+        assertTrue(s2.isEmpty());
 
-            intersection.addAll(diff1); intersection.addAll(diff2);
-            if (!intersection.equals(union))
-                fail("Set algebra identity 1 failed");
+        AddRandoms(s2, setSize);
 
-            if (new HashSet(union).hashCode() != union.hashCode())
-                fail("Incorrect hashCode computation.");
+        Set intersection = clone(s1, supplier);
+        intersection.retainAll(s2);
+        Set diff1 = clone(s1, supplier); diff1.removeAll(s2);
+        Set diff2 = clone(s2, supplier); diff2.removeAll(s1);
+        Set union = clone(s1, supplier); union.addAll(s2);
 
-            Iterator e = union.iterator();
-            while (e.hasNext())
-                if (!intersection.remove(e.next()))
-                    fail("Couldn't remove element from copy.");
-            if (!intersection.isEmpty())
-                fail("Copy nonempty after deleting all elements.");
+        if (diff1.removeAll(diff2))
+            fail("Set algebra identity 2 failed");
+        if (diff1.removeAll(intersection))
+            fail("Set algebra identity 3 failed");
+        if (diff2.removeAll(diff1))
+            fail("Set algebra identity 4 failed");
+        if (diff2.removeAll(intersection))
+            fail("Set algebra identity 5 failed");
+        if (intersection.removeAll(diff1))
+            fail("Set algebra identity 6 failed");
+        if (intersection.removeAll(diff1))
+            fail("Set algebra identity 7 failed");
 
-            e = union.iterator();
-            while (e.hasNext()) {
-                Object o = e.next();
-                if (!union.contains(o))
-                    fail("Set doesn't contain one of its elements.");
-                e.remove();
-                if (union.contains(o))
-                    fail("Set contains element after deletion.");
-            }
-            if (!union.isEmpty())
-                fail("Set nonempty after deleting all elements.");
+        intersection.addAll(diff1); intersection.addAll(diff2);
+        if (!intersection.equals(union))
+            fail("Set algebra identity 1 failed");
 
-            s1.clear();
-            if (!s1.isEmpty())
-                fail("Set nonempty after clear.");
+        if (new HashSet(union).hashCode() != union.hashCode())
+            fail("Incorrect hashCode computation.");
+
+        Iterator e = union.iterator();
+        while (e.hasNext())
+            if (!intersection.remove(e.next()))
+                fail("Couldn't remove element from copy.");
+        if (!intersection.isEmpty())
+            fail("Copy nonempty after deleting all elements.");
+
+        e = union.iterator();
+        while (e.hasNext()) {
+            Object o = e.next();
+            if (!union.contains(o))
+                fail("Set doesn't contain one of its elements.");
+            e.remove();
+            if (union.contains(o))
+                fail("Set contains element after deletion.");
         }
+        if (!union.isEmpty())
+            fail("Set nonempty after deleting all elements.");
+
+        s1.clear();
+        if (!s1.isEmpty())
+            fail("Set nonempty after clear.");
     }
 
     // Done inefficiently so as to exercise toArray
-    static Set clone(Set s) {
-        Set clone = newSet();
-        List arrayList = Arrays.asList(s.toArray());
+    static  Set clone(Set s, Supplier> supplier) {
+        Set clone = supplier.get();
+        List arrayList = Arrays.asList((T[]) s.toArray());
         clone.addAll(arrayList);
         if (!s.equals(clone))
             fail("Set not equal to copy.");
@@ -109,13 +120,6 @@
         return clone;
     }
 
-    static Set newSet() {
-        Set s = Collections.checkedSet(new HashSet(), Integer.class);
-        if (!s.isEmpty())
-            fail("New instance non empty.");
-        return s;
-    }
-
     static void AddRandoms(Set s, int n) {
         for (int i=0; i navigableSetsProvider() {
+        ArrayList iters = new ArrayList<>(makeCheckedSets());
+        iters.ensureCapacity(numItr * iters.size());
+        for(int each=1; each < numItr; each++) {
+            iters.addAll( makeCheckedSets());
+        }
+        return iters.iterator();
+    }
 
+    public static Collection makeCheckedSets() {
+        return Arrays.asList(
+            new Object[]{"Collections.checkedSet(HashSet)",
+                (Supplier) () -> {return Collections.checkedSet(new HashSet(), Integer.class);}},
+            new Object[]{"Collections.checkedSet(TreeSet(reverseOrder)",
+                (Supplier) () -> {return Collections.checkedSet(new TreeSet(Collections.reverseOrder()), Integer.class);}},
+            new Object[]{"Collections.checkedSet(TreeSet).descendingSet()",
+                (Supplier) () -> {return Collections.checkedSet(new TreeSet().descendingSet(), Integer.class);}},
+            new Object[]{"Collections.checkedNavigableSet(TreeSet)",
+                (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(), Integer.class);}},
+            new Object[]{"Collections.checkedNavigableSet(TreeSet(reverseOrder)",
+                (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(Collections.reverseOrder()), Integer.class);}},
+            new Object[]{"Collections.checkedNavigableSet().descendingSet()",
+                (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet().descendingSet(), Integer.class);}}
+            );
     }
 }
diff --git a/test/java/util/Collections/EmptyCollectionSerialization.java b/test/java/util/Collections/EmptyCollectionSerialization.java
--- a/test/java/util/Collections/EmptyCollectionSerialization.java
+++ b/test/java/util/Collections/EmptyCollectionSerialization.java
@@ -23,13 +23,20 @@
 
 /*
  * @test
- * @bug     4684279
+ * @bug     4684279 7129185
  * @summary Empty utility collections should be singletons
  * @author  Josh Bloch
+ * @run testng EmptyCollectionSerialization
  */
 
 import java.util.*;
+import java.util.function.Supplier;
 import java.io.*;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertSame;
 
 public class EmptyCollectionSerialization {
     private static Object patheticDeepCopy(Object o) throws Exception {
@@ -45,16 +52,42 @@
         return ois.readObject();
     }
 
-    private static boolean isSingleton(Object o) throws Exception {
-        return patheticDeepCopy(o) == o;
+    @Test(dataProvider="SerializableSingletons")
+    public static void serializableSingletons(String description, Supplier o) {
+        try {
+            assertSame(patheticDeepCopy(o.get()), o.get(), description + ": not a singleton");
+        } catch(Exception all) {
+            fail(description + ": Unexpected Exception", all);
+        }
     }
 
-    public static void main(String[] args) throws Exception {
-        if (!isSingleton(Collections.EMPTY_SET))
-            throw new Exception("EMPTY_SET");
-        if (!isSingleton(Collections.EMPTY_LIST))
-            throw new Exception("EMPTY_LIST");
-        if (!isSingleton(Collections.EMPTY_MAP))
-            throw new Exception("EMPTY_MAP");
+    @DataProvider(name = "SerializableSingletons", parallel = true)
+    public static Iterator navigableMapProvider() {
+        return makeSingletons().iterator();
+    }
+
+    public static Collection makeSingletons() {
+        return Arrays.asList(
+            new Object[]{"Collections.EMPTY_LIST",
+                (Supplier) () -> {return Collections.EMPTY_LIST;}},
+            new Object[]{"Collections.EMPTY_MAP",
+                (Supplier) () -> {return Collections.EMPTY_MAP;}},
+            new Object[]{"Collections.EMPTY_SET",
+                (Supplier) () -> {return Collections.EMPTY_SET;}},
+            new Object[]{"Collections.singletonMap()",
+                (Supplier) () -> {return Collections.emptyList();}},
+            new Object[]{"Collections.emptyMap()",
+                (Supplier) () -> {return Collections.emptyMap();}},
+            new Object[]{"Collections.emptySet()",
+                (Supplier) () -> {return Collections.emptySet();}},
+            new Object[]{"Collections.emptySortedSet()",
+                (Supplier) () -> {return Collections.emptySortedSet();}},
+            new Object[]{"Collections.emptySortedMap()",
+                (Supplier) () -> {return Collections.emptySortedMap();}},
+            new Object[]{"Collections.emptyNavigableSet()",
+                (Supplier) () -> {return Collections.emptyNavigableSet();}},
+            new Object[]{"Collections.emptyNavigableMap()",
+                (Supplier) () -> {return Collections.emptyNavigableMap();}}
+            );
     }
 }
diff --git a/test/java/util/Collections/EmptySortedSet.java b/test/java/util/Collections/EmptyNavigableSet.java
rename from test/java/util/Collections/EmptySortedSet.java
rename to test/java/util/Collections/EmptyNavigableSet.java
--- a/test/java/util/Collections/EmptySortedSet.java
+++ b/test/java/util/Collections/EmptyNavigableSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,329 +23,392 @@
 
 /*
  * @test
- * @bug 4533691
- * @summary Unit test for Collections.emptySortedSet
+ * @bug 4533691 7129185
+ * @summary Unit test for Collections.emptyNavigableSet
+ * @run testng EmptyNavigableSet
  */
-
-import java.lang.reflect.Method;
 import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.NavigableSet;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
 
-public class EmptySortedSet {
-    static int status = 0;
-    private static final String FAILED = " failed. ";
-    private static final String PERIOD = ".";
-    private final String thisClassName = this.getClass().getName();
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
 
-    public static void main(String[] args) throws Exception {
-        new EmptySortedSet();
+public class EmptyNavigableSet {
+
+    public static  void assertInstance(T actual, Class expected) {
+        assertInstance(expected.isInstance(actual), null);
     }
 
-    public EmptySortedSet() throws Exception {
-        run();
+    public static  void assertInstance(T actual, Class expected, String message) {
+        assertTrue(expected.isInstance(actual), ((null != message) ? message : "")
+            + " " + (actual == null ? "" : actual.getClass().getSimpleName()) + " != " + expected.getSimpleName() + ". ");
     }
 
-    /**
-     * Returns {@code true} if the {@link Object} passed in is an empty
-     * {@link SortedSet}.
-     *
-     * @param obj the object to test
-     * @return {@code true} if the {@link Object} is an empty {@link SortedSet}
-     *         otherwise {@code false}.
-     */
-    private boolean isEmptySortedSet(Object obj) {
-        boolean isEmptySortedSet = false;
+    public static  void assertEmptyNavigableSet(Object obj) {
+        assertInstance(obj, NavigableSet.class);
+        assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0));
+    }
 
-        // We determine if the object is an empty sorted set by testing if it's
-        // an instance of SortedSet, and if so, if it's empty.  Currently the
-        // testing doesn't include checks of the other methods.
-        if (obj instanceof SortedSet) {
-            SortedSet ss = (SortedSet) obj;
+    public static  void assertEmptyNavigableSet(Object obj, String message) {
+        assertInstance(obj, NavigableSet.class, message);
+        assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0),
+            ((null != message) ? message : "") + " Not empty. ");
+    }
 
-            if ((ss.isEmpty()) && (ss.size() == 0)) {
-                isEmptySortedSet = true;
-            }
+    public interface Thrower {
+
+        public void run() throws T;
+    }
+
+    public static  void assertThrows(Thrower thrower, Class throwable) {
+        assertThrows(thrower, throwable, null);
+    }
+
+    public static  void assertThrows(Thrower thrower, Class throwable, String message) {
+        Throwable result;
+        try {
+            thrower.run();
+            fail(((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". ");
+            return;
+        } catch (Throwable caught) {
+            result = caught;
         }
 
-        return isEmptySortedSet;
+        assertInstance(result, throwable, ((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". ");
     }
 
-    private void run() throws Exception {
-        Method[] methods = this.getClass().getDeclaredMethods();
+    public static final boolean isDescending(SortedSet set) {
+        if (null == set.comparator()) {
+            // natural order
+            return false;
+        }
 
-        for (int i = 0; i < methods.length; i++) {
-            Method method = methods[i];
-            String methodName = method.getName();
+        if (Collections.reverseOrder() == set.comparator()) {
+            // reverse natural order.
+            return true;
+        }
 
-            if (methodName.startsWith("test")) {
-                try {
-                    Object obj = method.invoke(this, new Object[0]);
-                } catch(Exception e) {
-                    throw new Exception(this.getClass().getName() + "." +
-                            methodName + " test failed, test exception "
-                            + "follows\n" + e.getCause());
-                }
-            }
+        if (set.comparator().equals(Collections.reverseOrder(Collections.reverseOrder(set.comparator())))) {
+            // it's a Collections.reverseOrder(Comparator).
+            return true;
         }
-    }
 
-    private void throwException(String methodName, String reason)
-            throws Exception
-    {
-        StringBuilder sb = new StringBuilder(thisClassName);
-        sb.append(PERIOD);
-        sb.append(methodName);
-        sb.append(FAILED);
-        sb.append(reason);
-        throw new Exception(sb.toString());
-    }
-
-    /**
-     *
-     */
-    private void test00() throws Exception {
-        //throwException("test00", "This test has not been implemented yet.");
+        throw new IllegalStateException("can't determine ordering for " + set);
     }
 
     /**
      * Tests that the comparator is {@code null}.
      */
-    private void testComparatorIsNull() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        Comparator comparator = sortedSet.comparator();
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testComparatorIsNull(String description, NavigableSet navigableSet) {
+        Comparator comparator = navigableSet.comparator();
 
-        if (comparator != null) {
-            throwException("testComparatorIsNull", "Comparator is not null.");
-        }
+        assertTrue(comparator == null || comparator == Collections.reverseOrder(), description + ": Comparator (" + comparator + ") is not null.");
+    }
+
+    /**
+     * Tests that contains requires Comparable
+     */
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testContainsRequiresComparable(String description, NavigableSet navigableSet) {
+        assertThrows(() -> {
+            navigableSet.contains(new Object());
+        },
+            ClassCastException.class,
+            description + ": Compareable should be required");
     }
 
     /**
      * Tests that the contains method returns {@code false}.
      */
-    private void testContains() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-
-        if (sortedSet.contains(new Object())) {
-            throwException("testContains", "Should not contain any elements.");
-        }
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testContains(String description, NavigableSet navigableSet) {
+        assertFalse(navigableSet.contains(new Integer(1)),
+            description + ": Should not contain any elements.");
     }
 
     /**
      * Tests that the containsAll method returns {@code false}.
      */
-    private void testContainsAll() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testContainsAll(String description, NavigableSet navigableSet) {
         TreeSet treeSet = new TreeSet();
         treeSet.add("1");
         treeSet.add("2");
         treeSet.add("3");
 
-        if (sortedSet.containsAll(treeSet)) {
-            throwException("testContainsAll",
-                    "Should not contain any elements.");
-        }
+        assertFalse(navigableSet.containsAll(treeSet), "Should not contain any elements.");
     }
 
     /**
      * Tests that the iterator is empty.
      */
-    private void testEmptyIterator() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        Iterator emptyIterator = sortedSet.iterator();
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testEmptyIterator(String description, NavigableSet navigableSet) {
+        Iterator emptyIterator = navigableSet.iterator();
 
-        if ((emptyIterator != null) && (emptyIterator.hasNext())) {
-            throwException("testEmptyIterator", "The iterator is not empty.");
-        }
+        assertFalse((emptyIterator != null) && (emptyIterator.hasNext()),
+            "The iterator is not empty.");
     }
 
     /**
      * Tests that the set is empty.
      */
-    private void testIsEmpty() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-
-        if ((sortedSet != null) && (!sortedSet.isEmpty())) {
-            throwException("testSizeIsZero", "The set is not empty.");
-        }
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testIsEmpty(String description, NavigableSet navigableSet) {
+        assertTrue(navigableSet.isEmpty(), "The set is not empty.");
     }
 
     /**
      * Tests that the first() method throws NoSuchElementException
      */
-    private void testFirst() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-
-        try {
-            sortedSet.first();
-            throwException("testFirst",
-                    "NoSuchElemenException was not thrown.");
-        } catch(NoSuchElementException nsee) {
-            // Do nothing
-        }
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testFirst(String description, NavigableSet navigableSet) {
+        assertThrows(() -> {
+            navigableSet.first();
+        }, NoSuchElementException.class, description);
     }
 
     /**
      * Tests the headSet() method.
      */
-    private void testHeadSet() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testHeadSet(String description, NavigableSet navigableSet) {
         SortedSet ss;
 
         try {
-            ss = sortedSet.headSet(null);
-            throwException("testHeadSet",
-                    "Must throw NullPointerException for null element");
-        } catch(NullPointerException npe) {
+            ss = navigableSet.headSet(null);
+            fail(description + ": Must throw NullPointerException for null element");
+        } catch (NullPointerException npe) {
             // Do nothing
         }
 
         try {
-            ss = sortedSet.headSet(new Object());
-            throwException("testHeadSet",
-                    "Must throw ClassCastException for non-Comparable element");
-        } catch(ClassCastException cce) {
+            ss = navigableSet.headSet(new Object());
+            fail(description + ": Must throw ClassCastException for non-Comparable element");
+        } catch (ClassCastException cce) {
             // Do nothing.
         }
 
-        ss = sortedSet.headSet("1");
+        ss = navigableSet.headSet("1", false);
 
-        if ((ss == null) || !isEmptySortedSet(ss)) {
-            throwException("testHeadSet",
-                    "Returned value is null or not an EmptySortedSet.");
-        }
+        assertEmptyNavigableSet(ss, description + ": Returned value is not empty navigable set.");
     }
 
     /**
      * Tests that the last() method throws NoSuchElementException
      */
-    private void testLast() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-
-        try {
-            sortedSet.last();
-            throwException("testLast",
-                    "NoSuchElemenException was not thrown.");
-        } catch(NoSuchElementException nsee) {
-            // Do nothing
-        }
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testLast(String description, NavigableSet navigableSet) {
+        assertThrows(() -> {
+            navigableSet.last();
+        }, NoSuchElementException.class, description);
     }
 
     /**
      * Tests that the size is 0.
      */
-    private void testSizeIsZero() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        int size = sortedSet.size();
-
-        if (size > 0) {
-            throwException("testSizeIsZero",
-                    "The size of the set is greater then 0.");
-        }
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testSizeIsZero(String description, NavigableSet navigableSet) {
+        assertTrue(0 == navigableSet.size(), "The size of the set is not 0.");
     }
 
     /**
      * Tests the subSet() method.
      */
-    private void testSubSet() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        SortedSet ss = sortedSet.headSet("1");
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testSubSet(String description, NavigableSet navigableSet) {
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(null, BigInteger.TEN);
+            },
+            NullPointerException.class,
+            description + ": Must throw NullPointerException for null element");
 
-        try {
-            ss = sortedSet.subSet(null, BigInteger.TEN);
-            ss = sortedSet.subSet(BigInteger.ZERO, null);
-            ss = sortedSet.subSet(null, null);
-            throwException("testSubSet",
-                    "Must throw NullPointerException for null element");
-        } catch(NullPointerException npe) {
-            // Do nothing
-        }
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(BigInteger.ZERO, null);
+            },
+            NullPointerException.class,
+            description + ": Must throw NullPointerException for null element");
 
-        try {
-            Object obj1 = new Object();
-            Object obj2 = new Object();
-            ss = sortedSet.subSet(obj1, BigInteger.TEN);
-            ss = sortedSet.subSet(BigInteger.ZERO, obj2);
-            ss = sortedSet.subSet(obj1, obj2);
-            throwException("testSubSet",
-                    "Must throw ClassCastException for parameter which is "
-                    + "not Comparable.");
-        } catch(ClassCastException cce) {
-            // Do nothing.
-        }
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(null, null);
+            },
+            NullPointerException.class,
+            description + ": Must throw NullPointerException for null element");
 
-        try {
-            ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.ZERO);
-            ss = sortedSet.subSet(BigInteger.TEN, BigInteger.ZERO);
-            throwException("testSubSet",
-                    "Must throw IllegalArgumentException when fromElement is "
-                    + "not less then then toElement.");
-        } catch(IllegalArgumentException iae) {
-            // Do nothing.
-        }
+        Object obj1 = new Object();
+        Object obj2 = new Object();
 
-        ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.TEN);
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(obj1, BigInteger.TEN);
+            },
+            ClassCastException.class, description
+            + ": Must throw ClassCastException for parameter which is not Comparable.");
 
-        if (!isEmptySortedSet(ss)) {
-            throw new Exception("Returned value is not empty sorted set.");
-        }
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(BigInteger.ZERO, obj2);
+            },
+            ClassCastException.class, description
+            + ": Must throw ClassCastException for parameter which is not Comparable.");
+
+        assertThrows(
+            () -> {
+                SortedSet ss = navigableSet.subSet(obj1, obj2);
+            },
+            ClassCastException.class, description
+            + ": Must throw ClassCastException for parameter which is not Comparable.");
+
+        // minimal range
+        navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, false);
+        navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, true);
+        navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, false);
+        navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, true);
+
+        Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO;
+        Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO;
+
+            assertThrows(
+                () -> {
+                    navigableSet.subSet(last, true, first, false);
+                },
+                IllegalArgumentException.class, description
+                + ": Must throw IllegalArgumentException when fromElement is not less then then toElement.");
+
+        navigableSet.subSet(first, true, last, false);
+    }
+
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testSubSetRanges(String description, NavigableSet navigableSet) {
+        Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO;
+        Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO;
+
+        NavigableSet subSet = navigableSet.subSet(first, true, last, true);
+
+        // same subset
+        subSet.subSet(first, true, last, true);
+
+        // slightly smaller
+        NavigableSet ns = subSet.subSet(first, false, last, false);
+        // slight exapansion
+        assertThrows(() -> {
+            ns.subSet(first, true, last, true);
+        },
+            IllegalArgumentException.class,
+            description + ": Expansion should not be allowed");
+
+        // much smaller
+        subSet.subSet(first, false, BigInteger.ONE, false);
+    }
+
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testheadSetRanges(String description, NavigableSet navigableSet) {
+        NavigableSet subSet = navigableSet.headSet(BigInteger.ONE, true);
+
+        // same subset
+        subSet.headSet(BigInteger.ONE, true);
+
+        // slightly smaller
+        NavigableSet ns = subSet.headSet(BigInteger.ONE, false);
+        // slight exapansion
+        assertThrows(() -> {
+            ns.headSet(BigInteger.ONE, true);
+        },
+            IllegalArgumentException.class,
+            description + ": Expansion should not be allowed");
+
+        // much smaller
+        subSet.headSet(isDescending(subSet) ? BigInteger.TEN : BigInteger.ZERO, true);
+    }
+
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testTailSetRanges(String description, NavigableSet navigableSet) {
+        NavigableSet subSet = navigableSet.tailSet(BigInteger.ONE, true);
+
+        // same subset
+        subSet.tailSet(BigInteger.ONE, true);
+
+        // slightly smaller
+        NavigableSet ns = subSet.tailSet(BigInteger.ONE, false);
+        // slight exapansion
+        assertThrows(() -> {
+            ns.tailSet(BigInteger.ONE, true);
+        },
+            IllegalArgumentException.class,
+            description + ": Expansion should not be allowed");
+
+        // much smaller
+        subSet.tailSet(isDescending(subSet) ? BigInteger.ZERO : BigInteger.TEN, false);
     }
 
     /**
      * Tests the tailSet() method.
      */
-    private void testTailSet() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        SortedSet ss;
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testTailSet(String description, NavigableSet navigableSet) {
+        assertThrows(() -> {
+            navigableSet.tailSet(null);
+        },
+            NullPointerException.class,
+            description + ": Must throw NullPointerException for null element");
 
-        try {
-            ss = sortedSet.tailSet(null);
-            throwException("testTailSet",
-                    "Must throw NullPointerException for null element");
-        } catch(NullPointerException npe) {
-            // Do nothing
-        }
+        assertThrows(() -> {
+            navigableSet.tailSet(new Object());
+        }, ClassCastException.class);
 
-        try {
-            SortedSet ss2 = sortedSet.tailSet(new Object());
-            throwException("testTailSet",
-                    "Must throw ClassCastException for non-Comparable element");
-        } catch(ClassCastException cce) {
-            // Do nothing.
-        }
+        NavigableSet ss = navigableSet.tailSet("1", true);
 
-        ss = sortedSet.tailSet("1");
-
-        if ((ss == null) || !isEmptySortedSet(ss)) {
-            throwException("testTailSet",
-                    "Returned value is null or not an EmptySortedSet.");
-        }
+        assertEmptyNavigableSet(ss, description + ": Returned value is not empty navigable set.");
     }
 
     /**
      * Tests that the array has a size of 0.
      */
-    private void testToArray() throws Exception {
-        SortedSet sortedSet = Collections.emptySortedSet();
-        Object[] emptySortedSetArray = sortedSet.toArray();
+    @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class)
+    public void testToArray(String description, NavigableSet navigableSet) {
+        Object[] emptyNavigableSetArray = navigableSet.toArray();
 
-        if ((emptySortedSetArray == null) || (emptySortedSetArray.length > 0)) {
-            throwException("testToArray",
-                    "Returned null array or array with length > 0.");
-        }
+        assertTrue(emptyNavigableSetArray.length == 0, "Returned non-empty Array.");
 
-        String[] strings = new String[2];
-        strings[0] = "1";
-        strings[1] = "2";
-        emptySortedSetArray = sortedSet.toArray(strings);
+        emptyNavigableSetArray = new Object[20];
 
-        if ((emptySortedSetArray == null) || (emptySortedSetArray[0] != null)) {
-            throwException("testToArray",
-                    "Returned null array or array with length > 0.");
-        }
+        Object[] result = navigableSet.toArray(emptyNavigableSetArray);
+
+        assertSame(emptyNavigableSetArray, result);
+
+        assertTrue(result[0] == null);
+    }
+
+    @DataProvider(name = "NavigableSet", parallel = true)
+    public static Iterator navigableSetsProvider() {
+        return makeNavigableSets().iterator();
+    }
+
+    public static Collection makeNavigableSets() {
+        return Arrays.asList(
+            new Object[]{"UnmodifiableNavigableSet(TreeSet)", Collections.unmodifiableNavigableSet(new TreeSet())},
+            new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet())},
+            new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet().descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet().descendingSet())},
+            new Object[]{"emptyNavigableSet()", Collections.emptyNavigableSet()},
+            new Object[]{"emptyNavigableSet().descendingSet()", Collections.emptyNavigableSet().descendingSet()},
+            new Object[]{"emptyNavigableSet().descendingSet().descendingSet()", Collections.emptyNavigableSet().descendingSet().descendingSet()}
+        );
     }
 }