src/share/classes/java/util/Collections.java

Print this page
rev 7019 : 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set}
Reviewed-by: duke

@@ -1210,10 +1210,74 @@
         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 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

@@ -1668,10 +1732,135 @@
 
         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> SortedMap<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 = -8806743815996713206L;
+
+        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 (NavigableMap<K,V>) 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 (NavigableMap<K,V>) unmodifiableNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive));
+        }
+
+        @Override
+        public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+            return (NavigableMap<K,V>) unmodifiableNavigableMap(nm.headMap(toKey, inclusive));
+        }
+
+        @Override
+        public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+            return (NavigableMap<K,V>) unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive));
+        }
+    }
 
     // Synch Wrappers
 
     /**
      * Returns a synchronized (thread-safe) collection backed by the specified

@@ -1943,10 +2132,114 @@
             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 <tt>subSet</tt>,
+     * <tt>headSet</tt>, or <tt>tailSet</tt> 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 = 8695801310862127406L;
+
+        private final NavigableSet<E> ns;
+
+        SynchronizedNavigableSet(NavigableSet<E> s) {
+            super(s);
+            ns = s;
+        }
+        SynchronizedNavigableSet(NavigableSet<E> s, Object mutex) {
+            super(s, mutex);
+            ns = s;
+        }
+        @Override public E lower(E e) { synchronized (mutex) {return ns.lower(e);} }
+        @Override public E floor(E e) { synchronized (mutex) {return ns.floor(e);} }
+        @Override public E ceiling(E e) { synchronized (mutex) {return ns.ceiling(e);} }
+        @Override public E higher(E e) { synchronized (mutex) {return ns.higher(e);} }
+        @Override public E pollFirst() { synchronized (mutex) {return ns.pollFirst();} }
+        @Override public E pollLast() { synchronized (mutex) {return ns.pollLast();}  }
+        @Override
+        public NavigableSet<E> descendingSet() {
+            synchronized (mutex) {
+            return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex);
+            }
+        }
+
+        @Override
+        public Iterator<E> descendingIterator() {
+            synchronized (mutex) {
+            return descendingSet().iterator();
+            }
+        }
+
+        @Override
+        public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+            synchronized (mutex) {
+            return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex);
+            }
+        }
+
+        @Override
+        public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+            synchronized (mutex) {
+            return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex);
+            }
+        }
+
+        @Override
+        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>
      *

@@ -2382,73 +2675,251 @@
         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.
+     * 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>
      *
-     * <p>The returned collection will be serializable if the specified
-     * collection is serializable.
+     * 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 <tt>subMap</tt>, <tt>headMap</tt> or
+     * <tt>tailMap</tt> 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>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.
+     * <p>The returned navigable map will be serializable if the specified
+     * navigable map is serializable.
      *
-     * @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
+     * @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 <E> Collection<E> checkedCollection(Collection<E> c,
+    public static <K,V> NavigableMap<K,V> synchronizedNavigableMap(NavigableMap<K,V> m) {
+        return new SynchronizedNavigableMap<>(m);
+    }
+
+
+    /**
+     * @serial include
+     */
+    static class SynchronizedNavigableMap<K,V>
+        extends SynchronizedSortedMap<K,V>
+        implements NavigableMap<K,V>
+    {
+        private static final long serialVersionUID = -8798146769416483793L;
+
+        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;
+        }
+
+        @Override
+        public Entry<K, V> lowerEntry(K key) {
+            synchronized (mutex) { return nm.lowerEntry(key); }
+        }
+
+        @Override
+        public K lowerKey(K key) {
+            synchronized (mutex) { return nm.lowerKey(key); }
+        }
+
+        @Override
+        public Entry<K, V> floorEntry(K key) {
+            synchronized (mutex) { return nm.floorEntry(key); }
+        }
+
+        @Override
+        public K floorKey(K key) {
+            synchronized (mutex) { return nm.floorKey(key); }
+        }
+
+        @Override
+        public Entry<K, V> ceilingEntry(K key) {
+            synchronized (mutex) { return nm.ceilingEntry(key); }
+        }
+
+        @Override
+        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) {
+            Entry<K,V> entry = (Entry<K,V>) nm.pollFirstEntry();
+            return (null == entry) ? null : entry;
+            }
+        }
+
+        @Override
+        public Entry<K, V> pollLastEntry() {
+            synchronized (mutex) {
+            Entry<K,V> entry = (Entry<K,V>) nm.pollLastEntry();
+            return (null == entry) ? null : entry;
+            }
+        }
+
+        @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<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
+     */
+    public static <E> Collection<E> checkedCollection(Collection<E> c,
                                                       Class<E> type) {
         return new CheckedCollection<>(c, type);
     }
 
     @SuppressWarnings("unchecked")

@@ -2726,10 +3197,112 @@
         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 = 1599911165492914959L;
+        private final NavigableSet<E> ns;
+
+        CheckedNavigableSet(NavigableSet<E> s, Class<E> type) {
+            super(s, type);
+            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() {
+            return ns.pollFirst();
+        }
+
+        @Override
+        public E pollLast() {
+            return ns.pollLast();
+        }
+
+        @Override
+        public NavigableSet<E> descendingSet() {
+            return checkedNavigableSet(ns.descendingSet(), type);
+        }
+
+        @Override
+        public Iterator<E> descendingIterator() {
+            return checkedNavigableSet(ns.descendingSet(), type).iterator();
+        }
+
+        @Override
+        public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+            return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type);
+        }
+
+        @Override
+        public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+            return checkedNavigableSet(ns.headSet(toElement, inclusive), type);
+        }
+
+        @Override
+        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

@@ -3323,37 +3896,201 @@
         public SortedMap<K,V> tailMap(K fromKey) {
             return checkedSortedMap(sm.tailMap(fromKey), keyType, valueType);
         }
     }
 
-    // Empty collections
-
     /**
-     * Returns an iterator that has no elements.  More precisely,
-     *
-     * <ul compact>
-     *
-     * <li>{@link Iterator#hasNext hasNext} always returns {@code
-     * false}.
+     * 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.
      *
-     * <li>{@link Iterator#next next} always throws {@link
-     * NoSuchElementException}.
+     * <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.
      *
-     * <li>{@link Iterator#remove remove} always throws {@link
-     * IllegalStateException}.
+     * <p>A discussion of the use of dynamically typesafe views may be
+     * found in the documentation for the {@link #checkedCollection
+     * checkedCollection} method.
      *
-     * </ul>
+     * <p>The returned map will be serializable if the specified map is
+     * serializable.
      *
-     * <p>Implementations of this method are permitted, but not
-     * required, to return the same object from multiple invocations.
+     * <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.
      *
-     * @return an empty iterator
-     * @since 1.7
+     * @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
      */
-    @SuppressWarnings("unchecked")
-    public static <T> Iterator<T> emptyIterator() {
-        return (Iterator<T>) EmptyIterator.EMPTY_ITERATOR;
+    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 = 1599671320688067438L;
+
+        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,
+     *
+     * <ul compact>
+     *
+     * <li>{@link Iterator#hasNext hasNext} always returns {@code
+     * false}.
+     *
+     * <li>{@link Iterator#next next} always throws {@link
+     * NoSuchElementException}.
+     *
+     * <li>{@link Iterator#remove remove} always throws {@link
+     * IllegalStateException}.
+     *
+     * </ul>
+     *
+     * <p>Implementations of this method are permitted, but not
+     * required, to return the same object from multiple invocations.
+     *
+     * @return an empty iterator
+     * @since 1.7
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Iterator<T> emptyIterator() {
+        return (Iterator<T>) EmptyIterator.EMPTY_ITERATOR;
     }
 
     private static class EmptyIterator<E> implements Iterator<E> {
         static final EmptyIterator<Object> EMPTY_ITERATOR
             = new EmptyIterator<>();

@@ -3522,110 +4259,164 @@
         private Object readResolve() {
             return EMPTY_SET;
         }
     }
 
+    private static final EmptyNavigableSet<?> EMPTY_NAVIGABLE_SET =
+        new EmptyNavigableSet<>();
+
     /**
      * 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&lt;String&gt; s = Collections.emptySortedSet();
-     * </pre>
-     * Implementation note:  Implementations of this method need not
-     * create a separate <tt>SortedSet</tt> object for each call.
+     * <p>This example illustrates the type-safe way to obtain an empty
+     * sorted set:
+     * <pre> {@code
+     *     SortedSet<String> s = Collections.emptySortedSet();
+     * }</pre>
+     *
+     * @implNote Implementations of this method need not create a separate
+     * {@code SortedSet} object for each call.
+     *
+     * @since 1.8
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> SortedSet<E> emptySortedSet() {
+        return (SortedSet<E>) EMPTY_NAVIGABLE_SET;
+    }
+
+    /**
+     * Returns the 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.
      *
      * @since 1.8
      */
     @SuppressWarnings("unchecked")
-    public static final <E> SortedSet<E> emptySortedSet() {
-        return (SortedSet<E>) new EmptySortedSet<>();
+    public static <E>NavigableSet<E> emptyNavigableSet() {
+        return (NavigableSet<E>) EMPTY_NAVIGABLE_SET;
     }
 
     /**
      * @serial include
      */
-    private static class EmptySortedSet<E>
-        extends AbstractSet<E>
-        implements SortedSet<E>, Serializable
+    private static class EmptyNavigableSet<E>
+        extends EmptySet<E>
+        implements NavigableSet<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]; }
+        // private static final long serialVersionUID = 6316515401502265487L;
 
-        public <E> E[] toArray(E[] a) {
-            if (a.length > 0)
-                a[0] = null;
-            return a;
+        public boolean contains(Object obj) {
+            Comparable<E> e = (Comparable<E>) obj;
+            return obj != e;
         }
 
         // Preserves singleton property
         private Object readResolve() {
-            return new EmptySortedSet<>();
+            return Collections.emptyNavigableSet();
         }
 
         @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);
+        public SortedSet<E> subSet(E fromElement, E toElement) {
+            return subSet(fromElement, true, toElement, false);
+        }
 
-            if (!(fromElement instanceof Comparable) ||
-                    !(toElement instanceof Comparable))
-            {
-                throw new ClassCastException();
+        @Override
+        public SortedSet<E> headSet(E toElement) {
+           return headSet(toElement, false);
             }
 
-            if ((((Comparable)fromElement).compareTo(toElement) >= 0) ||
-                    (((Comparable)toElement).compareTo(fromElement) < 0))
-            {
-                throw new IllegalArgumentException();
+        @Override
+        public SortedSet<E> tailSet(E fromElement) {
+            return tailSet(fromElement, true);
             }
 
-            return emptySortedSet();
+        @Override
+        public E first() {
+            throw new NoSuchElementException();
         }
 
         @Override
-        public SortedSet<E> headSet(Object toElement) {
-            Objects.requireNonNull(toElement);
+        public E last() {
+            throw new NoSuchElementException();
+        }
 
-            if (!(toElement instanceof Comparable)) {
-                throw new ClassCastException();
+        @Override
+        public E lower(E e) {
+            Objects.requireNonNull(e);
+            return null;
             }
 
-            return emptySortedSet();
+        @Override
+        public E floor(E e) {
+            Objects.requireNonNull(e);
+            return null;
         }
 
         @Override
-        public SortedSet<E> tailSet(Object fromElement) {
-            Objects.requireNonNull(fromElement);
+        public E ceiling(E e) {
+            Objects.requireNonNull(e);
+            return null;
+        }
 
-            if (!(fromElement instanceof Comparable)) {
-                throw new ClassCastException();
+        @Override
+        public E higher(E e) {
+            Objects.requireNonNull(e);
+            return null;
             }
 
-            return emptySortedSet();
+        @Override
+        public E pollFirst() {
+            return null;
         }
 
         @Override
-        public E first() {
-            throw new NoSuchElementException();
+        public E pollLast() {
+            return null;
         }
 
         @Override
-        public E last() {
-            throw new NoSuchElementException();
+        public NavigableSet<E> descendingSet() {
+                return new BoundedEmptyNavigableSet(null, true, null, true, true);
+        }
+
+        @Override
+        public Iterator<E> descendingIterator() { return emptyIterator(); }
+
+        @Override
+        public NavigableSet<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);
+        }
+
+        @Override
+        public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+            return new BoundedEmptyNavigableSet(
+                null, true,
+                (Comparable<E>) Objects.requireNonNull(toElement), inclusive,
+                false);
+        }
+
+        @Override
+        public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
+            return new BoundedEmptyNavigableSet(
+                (Comparable<E>) Objects.requireNonNull(fromElement), inclusive,
+                null, true, false);
         }
 
         // Override default methods in Collection
         @Override
         public void forEach(Consumer<? super E> action) {

@@ -3736,10 +4527,274 @@
         private Object readResolve() {
             return EMPTY_LIST;
         }
     }
 
+    private static class BoundedEmptyNavigableSet<E> extends EmptyNavigableSet<E>
+        implements Serializable {
+
+        final boolean descending;
+        final boolean lowerInclusive;
+        final boolean upperInclusive;
+        final Comparable<E> lowerBound;
+        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))  {
+                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<E> comparator() {
+            return descending ? Collections.reverseOrder() : null;
+        }
+
+        public NavigableSet<E> descendingSet() {
+            return new BoundedEmptyNavigableSet(upperBound, upperInclusive, lowerBound, lowerInclusive, !descending);
+        }
+
+        @Override
+        public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
+            return new BoundedEmptyNavigableSet(inBounds(fromElement), fromInclusive, inBounds(toElement), toInclusive, descending);
+        }
+
+        @Override
+        public NavigableSet<E> headSet(E toElement, boolean inclusive) {
+            return new BoundedEmptyNavigableSet(lowerBound, lowerInclusive, inBounds(toElement), inclusive, descending);
+        }
+
+        @Override
+        public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
+            return new BoundedEmptyNavigableSet(inBounds(fromElement), inclusive, upperBound, upperInclusive, descending);
+        }
+
+        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);
+        }
+    }
+
+private static class BoundedEmptyNavigableMap<K,V> extends EmptyNavigableMap<K,V>
+        implements Serializable {
+
+        final boolean descending;
+        final boolean lowerInclusive;
+        final boolean upperInclusive;
+        final Comparable<K> lowerBound;
+        final Comparable<K> upperBound;
+
+        public BoundedEmptyNavigableMap(
+            Comparable<K> fromElement, boolean lowerInclusive,
+            Comparable<K> toElement, boolean upperInclusive,
+            boolean descending) {
+            this.descending = descending;
+
+            if ((fromElement != null) && (toElement != null))  {
+                int fromCompared = Integer.signum(fromElement.compareTo((K)toElement));
+                int toCompared = Integer.signum(toElement.compareTo((K)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 K> comparator() {
+            return descending ? Collections.reverseOrder() : null;
+        }
+
+        @Override
+        public boolean containsKey(Object obj) {
+            Comparable<K> e = (Comparable<K>) obj;
+            return obj != e;
+        }
+
+        private Comparable<K> inBounds(K 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<K>)Objects.requireNonNull(element);
+        }
+
+        @Override
+        public NavigableMap<K, V> descendingMap() {
+            if (upperBound == null && lowerBound == null && descending) {
+                return Collections.emptyNavigableMap();
+            }
+            return new BoundedEmptyNavigableMap(upperBound, upperInclusive, lowerBound, lowerInclusive, !descending);
+        }
+
+        @Override
+        public NavigableSet<K> navigableKeySet() {
+            if (upperBound == null && lowerBound == null && !descending) {
+                return Collections.emptyNavigableSet();
+            }
+            return new BoundedEmptyNavigableSet(lowerBound, lowerInclusive, upperBound, upperInclusive, descending);
+        }
+
+        @Override
+        public NavigableSet<K> descendingKeySet() {
+            if (upperBound == null && lowerBound == null && descending) {
+                return Collections.emptyNavigableSet();
+            }
+            return new BoundedEmptyNavigableSet(upperBound, upperInclusive, lowerBound, lowerInclusive, !descending);
+        }
+
+        @Override
+        public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+            return new BoundedEmptyNavigableMap(inBounds(fromKey), fromInclusive, inBounds(toKey), toInclusive, descending);
+        }
+
+        @Override
+        public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+            return new BoundedEmptyNavigableMap(lowerBound, lowerInclusive, inBounds(toKey), inclusive, descending);
+        }
+
+        @Override
+        public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+            return new BoundedEmptyNavigableMap(inBounds(fromKey), inclusive, upperBound, upperInclusive, descending);
+        }
+    }
+
     /**
      * The empty map (immutable).  This map is serializable.
      *
      * @see #emptyMap()
      * @since 1.3

@@ -3748,11 +4803,11 @@
     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:
+     * <p>This example illustrates the type-safe way to obtain an empty map:
      * <pre>
      *     Map&lt;String, Date&gt; s = Collections.emptyMap();
      * </pre>
      * Implementation note:  Implementations of this method need not
      * create a separate <tt>Map</tt> object for each call.   Using this

@@ -3766,10 +4821,50 @@
     public static final <K,V> Map<K,V> emptyMap() {
         return (Map<K,V>) EMPTY_MAP;
     }
 
     /**
+     * Returns the 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.
+     *
+     * @since 1.8
+     */
+    @SuppressWarnings("unchecked")
+    public static final <K,V> SortedMap<K,V> emptySortedMap() {
+        return (SortedMap<K,V>) EMPTY_NAVIGABLE_MAP;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final NavigableMap EMPTY_NAVIGABLE_MAP =
+            new EmptyNavigableMap();
+
+    /**
+     * Returns the 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.
+     *
+     * @since 1.8
+     */
+    @SuppressWarnings("unchecked")
+    public static final <K,V> NavigableMap<K,V> emptyNavigableMap() {
+        return (NavigableMap<K,V>) EMPTY_NAVIGABLE_MAP;
+    }
+
+    /**
      * @serial include
      */
     private static class EmptyMap<K,V>
         extends AbstractMap<K,V>
         implements Serializable

@@ -3856,10 +4951,143 @@
         private Object readResolve() {
             return EMPTY_MAP;
         }
     }
 
+    private static class EmptyNavigableMap<K,V> extends EmptyMap<K,V>
+        implements NavigableMap<K,V>, Serializable {
+
+        EmptyNavigableMap() {
+
+        }
+
+        // Preserves singleton property
+        private Object readResolve() {
+            return Collections.emptyNavigableMap();
+        }
+
+        @Override
+        public Entry<K, V> lowerEntry(K key) {
+            return null;
+        }
+
+        @Override
+        public K lowerKey(K key) {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> floorEntry(K key) {
+            return null;
+        }
+
+        @Override
+        public K floorKey(K key) {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> ceilingEntry(K key) {
+            return null;
+        }
+
+        @Override
+        public K ceilingKey(K key) {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> higherEntry(K key) {
+            return null;
+        }
+
+        @Override
+        public K higherKey(K key) {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> firstEntry() {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> lastEntry() {
+            return null;
+        }
+
+        @Override
+        public Entry<K, V> pollFirstEntry() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Entry<K, V> pollLastEntry() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NavigableMap<K, V> descendingMap() {
+            return new BoundedEmptyNavigableMap<>(null, true, null, true, true);
+        }
+
+        @Override
+        public NavigableSet<K> navigableKeySet() {
+            return Collections.emptyNavigableSet();
+        }
+
+        @Override
+        public NavigableSet<K> descendingKeySet() {
+            return Collections.<K>emptyNavigableSet().descendingSet();
+        }
+
+        @Override
+        public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
+            return new BoundedEmptyNavigableMap<>((Comparable<K>) fromKey, fromInclusive, (Comparable<K>) toKey, toInclusive, false);
+        }
+
+        @Override
+        public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
+            return new BoundedEmptyNavigableMap<>(null, false, (Comparable<K>) toKey, inclusive, false);
+        }
+
+        @Override
+        public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
+            return new BoundedEmptyNavigableMap<>((Comparable<K>) fromKey, inclusive, null, false, false);
+        }
+
+        @Override
+        public SortedMap<K, V> subMap(K fromKey, K toKey) {
+            return subMap(fromKey, true, toKey, false);
+        }
+
+        @Override
+        public SortedMap<K, V> headMap(K toKey) {
+            return headMap(toKey, false);
+        }
+
+        @Override
+        public SortedMap<K, V> tailMap(K fromKey) {
+            return tailMap(fromKey, true);
+        }
+
+        @Override
+        public Comparator<? super K> comparator() {
+            return null;
+        }
+
+        @Override
+        public K firstKey() {
+            return null;
+        }
+
+        @Override
+        public K lastKey() {
+            return null;
+        }
+    }
+
     // Singleton collections
 
     /**
      * Returns an immutable set containing only the specified object.
      * The returned set is serializable.