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

Print this page
rev 7291 : 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set}
Reviewed-by: dmocek, martin

*** 25,34 **** --- 25,35 ---- package java.util; import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.IOException; + import java.io.InvalidObjectException; import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function;
*** 134,144 **** * input array. It is well-suited to merging two or more sorted arrays: * simply concatenate the arrays and sort the resulting array. * * <p>The implementation was adapted from Tim Peters's list sort for Python * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt"> ! * TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. * * <p>This implementation dumps the specified list into an array, sorts --- 135,145 ---- * input array. It is well-suited to merging two or more sorted arrays: * simply concatenate the arrays and sort the resulting array. * * <p>The implementation was adapted from Tim Peters's list sort for Python * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt"> ! * TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. * * <p>This implementation dumps the specified list into an array, sorts
*** 195,205 **** * input array. It is well-suited to merging two or more sorted arrays: * simply concatenate the arrays and sort the resulting array. * * <p>The implementation was adapted from Tim Peters's list sort for Python * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt"> ! * TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. * * <p>This implementation dumps the specified list into an array, sorts --- 196,206 ---- * input array. It is well-suited to merging two or more sorted arrays: * simply concatenate the arrays and sort the resulting array. * * <p>The implementation was adapted from Tim Peters's list sort for Python * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt"> ! * TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. * * <p>This implementation dumps the specified list into an array, sorts
*** 1210,1219 **** --- 1211,1286 ---- public E first() {return ss.first();} public E last() {return ss.last();} } /** + * Returns an unmodifiable view of the specified navigable set. This method + * allows modules to provide users with "read-only" access to internal + * navigable sets. Query operations on the returned navigable set "read + * through" to the specified navigable set. Attempts to modify the returned + * navigable set, whether direct, via its iterator, or via its + * {@code subSet}, <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 + * navigable set is serializable. + * + * @param s the navigable set for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable set + * @since 1.8 + */ + public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s) { + return new UnmodifiableNavigableSet<>(s); + } + + /** + * @serial include + */ + static class UnmodifiableNavigableSet<E> + extends UnmodifiableSortedSet<E> + implements NavigableSet<E>, Serializable { + + private static final long serialVersionUID = -6027448201786391929L; + + private final NavigableSet<E> ns; + + UnmodifiableNavigableSet(NavigableSet<E> s) {super(s); ns = s;} + + @Override public E lower(E e) { return ns.lower(e); } + @Override public E floor(E e) { return ns.floor(e); } + @Override public E ceiling(E e) { return ns.ceiling(e); } + @Override public E higher(E e) { return ns.higher(e); } + @Override public E pollFirst() { throw new UnsupportedOperationException(); } + @Override public E pollLast() { throw new UnsupportedOperationException(); } + @Override + public NavigableSet<E> descendingSet() { + return new UnmodifiableNavigableSet<>(ns.descendingSet()); + } + + @Override + public Iterator<E> descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new UnmodifiableNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet<E> headSet(E toElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>(ns.headSet(toElement, inclusive)); + } + + @Override + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>(ns.tailSet(fromElement, inclusive)); + } + } + + /** * Returns an unmodifiable view of the specified list. This method allows * modules to provide users with "read-only" access to internal * lists. Query operations on the returned list "read through" to the * specified list, and attempts to modify the returned list, whether * direct or via its iterator, result in an
*** 1236,1245 **** --- 1303,1313 ---- * @serial include */ static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> { private static final long serialVersionUID = -283967356065247728L; + final List<? extends E> list; UnmodifiableList(List<? extends E> list) { super(list); this.list = list;
*** 1650,1677 **** implements SortedMap<K,V>, Serializable { private static final long serialVersionUID = -8806743815996713206L; private final SortedMap<K, ? extends V> sm; ! UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m;} ! public Comparator<? super K> comparator() {return sm.comparator();} ! public SortedMap<K,V> subMap(K fromKey, K toKey) { ! return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); } ! public SortedMap<K,V> headMap(K toKey) { ! return new UnmodifiableSortedMap<>(sm.headMap(toKey)); } ! public SortedMap<K,V> tailMap(K fromKey) { ! return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); } ! public K firstKey() {return sm.firstKey();} ! public K lastKey() {return sm.lastKey();} } // Synch Wrappers /** * Returns a synchronized (thread-safe) collection backed by the specified --- 1718,1828 ---- implements SortedMap<K,V>, Serializable { private static final long serialVersionUID = -8806743815996713206L; private final SortedMap<K, ? extends V> sm; ! UnmodifiableSortedMap(SortedMap<K, ? extends V> m) {super(m); sm = m; } ! public Comparator<? super K> comparator() { return sm.comparator(); } ! public SortedMap<K,V> subMap(K fromKey, K toKey) ! { return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); } ! public SortedMap<K,V> headMap(K toKey) ! { return new UnmodifiableSortedMap<>(sm.headMap(toKey)); } ! public SortedMap<K,V> tailMap(K fromKey) ! { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); } ! public K firstKey() { return sm.firstKey(); } ! public K lastKey() { return sm.lastKey(); } ! } ! /** ! * Returns an unmodifiable view of the specified navigable map. This method ! * allows modules to provide users with "read-only" access to internal ! * navigable maps. Query operations on the returned navigable map "read ! * through" to the specified navigable map. Attempts to modify the returned ! * navigable map, whether direct, via its collection views, or via its ! * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in ! * an {@code UnsupportedOperationException}.<p> ! * ! * The returned navigable map will be serializable if the specified ! * navigable map is serializable. ! * ! * @param m the navigable map for which an unmodifiable view is to be ! * returned ! * @return an unmodifiable view of the specified navigable map ! * @since 1.8 ! */ ! public static <K,V> NavigableMap<K,V> unmodifiableNavigableMap(NavigableMap<K, ? extends V> m) { ! return new UnmodifiableNavigableMap<>(m); ! } ! /** ! * @serial include ! */ ! static class UnmodifiableNavigableMap<K,V> ! extends UnmodifiableSortedMap<K,V> ! implements NavigableMap<K,V>, Serializable { ! private static final long serialVersionUID = -4858195264774772197L; ! ! private final NavigableMap<K, ? extends V> nm; ! ! UnmodifiableNavigableMap(NavigableMap<K, ? extends V> m) ! {super(m); nm = m;} ! ! public Entry<K, V> lowerEntry(K key) { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lowerEntry(key)); } ! ! public K lowerKey(K key) { return nm.lowerKey(key); } ! ! public Entry<K, V> floorEntry(K key) { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.floorEntry(key)); } ! ! public K floorKey(K key) { return nm.floorKey(key); } ! ! public Entry<K, V> ceilingEntry(K key) { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.ceilingEntry(key)); ! } ! ! public K ceilingKey(K key) { return nm.ceilingKey(key); } ! ! public Entry<K, V> higherEntry(K key) { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.higherEntry(key)); ! } ! ! public K higherKey(K key) { return nm.higherKey(key); } ! ! public Entry<K, V> firstEntry() { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.firstEntry()); ! } ! ! public Entry<K, V> lastEntry() { ! return new UnmodifiableEntrySet.UnmodifiableEntry(nm.lastEntry()); ! } ! ! public Entry<K, V> pollFirstEntry() ! { throw new UnsupportedOperationException(); } ! public Entry<K, V> pollLastEntry() ! { throw new UnsupportedOperationException(); } ! public NavigableMap<K, V> descendingMap() ! { return unmodifiableNavigableMap(nm.descendingMap()); } ! public NavigableSet<K> navigableKeySet() ! { return unmodifiableNavigableSet(nm.navigableKeySet()); } ! public NavigableSet<K> descendingKeySet() ! { return unmodifiableNavigableSet(nm.descendingKeySet()); } ! ! public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { ! return unmodifiableNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive)); } ! public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { ! return unmodifiableNavigableMap(nm.headMap(toKey, inclusive)); } + public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { + return unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive)); + } + } // Synch Wrappers /** * Returns a synchronized (thread-safe) collection backed by the specified
*** 1721,1738 **** final Collection<E> c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<E> c) { ! if (c==null) ! throw new NullPointerException(); ! this.c = c; mutex = this; } SynchronizedCollection(Collection<E> c, Object mutex) { ! this.c = c; ! this.mutex = mutex; } public int size() { synchronized (mutex) {return c.size();} } --- 1872,1888 ---- final Collection<E> c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<E> c) { ! this.c = Objects.requireNonNull(c); mutex = this; } + SynchronizedCollection(Collection<E> c, Object mutex) { ! this.c = Objects.requireNonNull(c); ! this.mutex = Objects.requireNonNull(mutex); } public int size() { synchronized (mutex) {return c.size();} }
*** 1943,1952 **** --- 2093,2200 ---- synchronized (mutex) {return ss.last();} } } /** + * Returns a synchronized (thread-safe) navigable set backed by the + * specified navigable set. In order to guarantee serial access, it is + * critical that <strong>all</strong> access to the backing navigable set is + * accomplished through the returned navigable set (or its views).<p> + * + * It is imperative that the user manually synchronize on the returned + * navigable set when iterating over it or any of its {@code subSet}, + * {@code headSet}, or {@code tailSet} views. + * <pre> + * NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet()); + * ... + * synchronized (s) { + * Iterator i = s.iterator(); // Must be in the synchronized block + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre> + * or: + * <pre> + * NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet()); + * NavigableSet s2 = s.headSet(foo, true); + * ... + * synchronized (s) { // Note: s, not s2!!! + * Iterator i = s2.iterator(); // Must be in the synchronized block + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre> + * Failure to follow this advice may result in non-deterministic behavior. + * + * <p>The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param s the navigable set to be "wrapped" in a synchronized navigable + * set + * @return a synchronized view of the specified navigable set + * @since 1.8 + */ + public static <T> NavigableSet<T> synchronizedNavigableSet(NavigableSet<T> s) { + return new SynchronizedNavigableSet<>(s); + } + + /** + * @serial include + */ + static class SynchronizedNavigableSet<E> + extends SynchronizedSortedSet<E> + implements NavigableSet<E> + { + private static final long serialVersionUID = -5505529816273629798L; + + private final NavigableSet<E> ns; + + SynchronizedNavigableSet(NavigableSet<E> s) { + super(s); + ns = s; + } + + SynchronizedNavigableSet(NavigableSet<E> s, Object mutex) { + super(s, mutex); + ns = s; + } + public E lower(E e) { synchronized (mutex) {return ns.lower(e);} } + public E floor(E e) { synchronized (mutex) {return ns.floor(e);} } + public E ceiling(E e) { synchronized (mutex) {return ns.ceiling(e);} } + public E higher(E e) { synchronized (mutex) {return ns.higher(e);} } + public E pollFirst() { synchronized (mutex) {return ns.pollFirst();} } + public E pollLast() { synchronized (mutex) {return ns.pollLast();} } + + public NavigableSet<E> descendingSet() { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex); + } + } + + public Iterator<E> descendingIterator() + { synchronized (mutex) { return descendingSet().iterator(); } } + + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex); + } + } + + public NavigableSet<E> headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex); + } + } + + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive)); + } + } + } + + /** * Returns a synchronized (thread-safe) list backed by the specified * list. In order to guarantee serial access, it is critical that * <strong>all</strong> access to the backing list is accomplished * through the returned list.<p> *
*** 2151,2163 **** private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { ! if (m==null) ! throw new NullPointerException(); ! this.m = m; mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; --- 2399,2409 ---- private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { ! this.m = Objects.requireNonNull(m); mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m;
*** 2332,2342 **** */ public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) { return new SynchronizedSortedMap<>(m); } - /** * @serial include */ static class SynchronizedSortedMap<K,V> extends SynchronizedMap<K,V> --- 2578,2587 ----
*** 2382,2391 **** --- 2627,2772 ---- public K lastKey() { synchronized (mutex) {return sm.lastKey();} } } + /** + * Returns a synchronized (thread-safe) navigable map backed by the + * specified navigable map. In order to guarantee serial access, it is + * critical that <strong>all</strong> access to the backing navigable map is + * accomplished through the returned navigable map (or its views).<p> + * + * It is imperative that the user manually synchronize on the returned + * navigable map when iterating over any of its collection views, or the + * collections views of any of its {@code subMap}, {@code headMap} or + * {@code tailMap} views. + * <pre> + * NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap()); + * ... + * Set s = m.keySet(); // Needn't be in synchronized block + * ... + * synchronized (m) { // Synchronizing on m, not s! + * Iterator i = s.iterator(); // Must be in synchronized block + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre> + * or: + * <pre> + * NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap()); + * NavigableMap m2 = m.subMap(foo, true, bar, false); + * ... + * Set s2 = m2.keySet(); // Needn't be in synchronized block + * ... + * synchronized (m) { // Synchronizing on m, not m2 or s2! + * Iterator i = s.iterator(); // Must be in synchronized block + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre> + * Failure to follow this advice may result in non-deterministic behavior. + * + * <p>The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param m the navigable map to be "wrapped" in a synchronized navigable + * map + * @return a synchronized view of the specified navigable map. + * @since 1.8 + */ + public static <K,V> NavigableMap<K,V> synchronizedNavigableMap(NavigableMap<K,V> m) { + return new SynchronizedNavigableMap<>(m); + } + + /** + * A synchronized NavigableMap. + * + * @serial include + */ + static class SynchronizedNavigableMap<K,V> + extends SynchronizedSortedMap<K,V> + implements NavigableMap<K,V> + { + private static final long serialVersionUID = 699392247599746807L; + + private final NavigableMap<K,V> nm; + + SynchronizedNavigableMap(NavigableMap<K,V> m) { + super(m); + nm = m; + } + SynchronizedNavigableMap(NavigableMap<K,V> m, Object mutex) { + super(m, mutex); + nm = m; + } + + public Entry<K, V> lowerEntry(K key) + { synchronized (mutex) { return nm.lowerEntry(key); } } + public K lowerKey(K key) + { synchronized (mutex) { return nm.lowerKey(key); } } + public Entry<K, V> floorEntry(K key) + { synchronized (mutex) { return nm.floorEntry(key); } } + public K floorKey(K key) + { synchronized (mutex) { return nm.floorKey(key); } } + public Entry<K, V> ceilingEntry(K key) + { synchronized (mutex) { return nm.ceilingEntry(key); } } + public K ceilingKey(K key) + { synchronized (mutex) { return nm.ceilingKey(key); } } + public Entry<K, V> higherEntry(K key) + { synchronized (mutex) { return nm.higherEntry(key); } } + public K higherKey(K key) + { synchronized (mutex) { return nm.higherKey(key); } } + public Entry<K, V> firstEntry() + { synchronized (mutex) { return nm.firstEntry(); } } + public Entry<K, V> lastEntry() + { synchronized (mutex) { return nm.lastEntry(); } } + public Entry<K, V> pollFirstEntry() + { synchronized (mutex) { return nm.pollFirstEntry(); } } + public Entry<K, V> pollLastEntry() + { synchronized (mutex) { return nm.pollLastEntry(); } } + + public NavigableMap<K, V> descendingMap() { + synchronized (mutex) { + return (NavigableMap<K,V>) + new SynchronizedNavigableMap(nm.descendingMap(), mutex); + } + } + + public NavigableSet<K> navigableKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex); + } + } + + public NavigableSet<K> descendingKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex); + } + } + + public 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); + } + } + + public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return (NavigableMap<K,V>) new SynchronizedNavigableMap( + nm.headMap(toKey, inclusive), mutex); + } + } + + 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
*** 2413,2428 **** * 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. --- 2794,2809 ---- * as to the real source of the problem. If the problem is reproducible, * one can quickly determine its source by temporarily modifying the * program to wrap the collection with a dynamically typesafe view. * For example, this declaration: * <pre> {@code ! * Collection<String> c = new HashSet<>(); * }</pre> * may be replaced temporarily by this one: * <pre> {@code * Collection<String> c = Collections.checkedCollection( ! * new HashSet<>(), String.class); * }</pre> * Running the program again will cause it to fail at the point where * an incorrectly typed element is inserted into the collection, clearly * identifying the source of the problem. Once the problem is fixed, the * modified declaration may be reverted back to the original.
*** 2704,2713 **** --- 3085,3095 ---- */ static class CheckedSortedSet<E> extends CheckedSet<E> implements SortedSet<E>, Serializable { private static final long serialVersionUID = 1599911165492914959L; + private final SortedSet<E> ss; CheckedSortedSet(SortedSet<E> s, Class<E> type) { super(s, type); ss = s;
*** 2726,2735 **** --- 3108,3188 ---- public SortedSet<E> tailSet(E fromElement) { return checkedSortedSet(ss.tailSet(fromElement), type); } } + /** + * Returns a dynamically typesafe view of the specified navigable set. + * Any attempt to insert an element of the wrong type will result in an + * immediate {@link ClassCastException}. Assuming a navigable set + * contains no incorrectly typed elements prior to the time a + * dynamically typesafe view is generated, and that all subsequent + * access to the navigable set takes place through the view, it is + * <em>guaranteed</em> that the navigable set cannot contain an incorrectly + * typed element. + * + * <p>A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + * <p>The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * <p>Since {@code null} is considered to be a value of any reference + * type, the returned navigable set permits insertion of null elements + * whenever the backing sorted set does. + * + * @param s the navigable set for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code s} is permitted to hold + * @return a dynamically typesafe view of the specified navigable set + * @since 1.8 + */ + public static <E> NavigableSet<E> checkedNavigableSet(NavigableSet<E> s, + Class<E> type) { + return new CheckedNavigableSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedNavigableSet<E> extends CheckedSortedSet<E> + implements NavigableSet<E>, Serializable + { + private static final long serialVersionUID = -5429120189805438922L; + + private final NavigableSet<E> ns; + + CheckedNavigableSet(NavigableSet<E> s, Class<E> type) { + super(s, type); + ns = s; + } + + public E lower(E e) { return ns.lower(e); } + public E floor(E e) { return ns.floor(e); } + public E ceiling(E e) { return ns.ceiling(e); } + public E higher(E e) { return ns.higher(e); } + public E pollFirst() { return ns.pollFirst(); } + public E pollLast() {return ns.pollLast(); } + public NavigableSet<E> descendingSet() + { return checkedNavigableSet(ns.descendingSet(), type); } + public Iterator<E> descendingIterator() + {return checkedNavigableSet(ns.descendingSet(), type).iterator(); } + + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type); + } + + public NavigableSet<E> headSet(E toElement, boolean inclusive) { + return checkedNavigableSet(ns.headSet(toElement, inclusive), type); + } + + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { + return checkedNavigableSet(ns.tailSet(fromElement, inclusive), type); + } + } + /** * Returns a dynamically typesafe view of the specified list. * Any attempt to insert an element of the wrong type will result in * an immediate {@link ClassCastException}. Assuming a list contains * no incorrectly typed elements prior to the time a dynamically typesafe
*** 2938,2952 **** return "Attempt to insert " + value.getClass() + " value into map with value type " + valueType; } CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) { ! if (m == null || keyType == null || valueType == null) ! throw new NullPointerException(); ! this.m = m; ! this.keyType = keyType; ! this.valueType = valueType; } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean containsKey(Object key) { return m.containsKey(key); } --- 3391,3403 ---- return "Attempt to insert " + value.getClass() + " value into map with value type " + valueType; } CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) { ! this.m = Objects.requireNonNull(m); ! this.keyType = Objects.requireNonNull(keyType); ! this.valueType = Objects.requireNonNull(valueType); } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean containsKey(Object key) { return m.containsKey(key); }
*** 3323,3339 **** 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}. * * <li>{@link Iterator#next next} always throws {@link * NoSuchElementException}. --- 3774,3935 ---- public SortedMap<K,V> tailMap(K fromKey) { return checkedSortedMap(sm.tailMap(fromKey), keyType, valueType); } } /** ! * Returns a dynamically typesafe view of the specified navigable map. ! * Any attempt to insert a mapping whose key or value have the wrong ! * type will result in an immediate {@link ClassCastException}. ! * Similarly, any attempt to modify the value currently associated with ! * a key will result in an immediate {@link ClassCastException}, ! * whether the modification is attempted directly through the map ! * itself, or through a {@link Map.Entry} instance obtained from the ! * map's {@link Map#entrySet() entry set} view. ! * ! * <p>Assuming a map contains no incorrectly typed keys or values ! * prior to the time a dynamically typesafe view is generated, and ! * that all subsequent access to the map takes place through the view ! * (or one of its collection views), it is <em>guaranteed</em> that the ! * map cannot contain an incorrectly typed key or value. ! * ! * <p>A discussion of the use of dynamically typesafe views may be ! * found in the documentation for the {@link #checkedCollection ! * checkedCollection} method. ! * ! * <p>The returned map will be serializable if the specified map is ! * serializable. ! * ! * <p>Since {@code null} is considered to be a value of any reference ! * type, the returned map permits insertion of null keys or values ! * whenever the backing map does. ! * ! * @param <K> type of map keys ! * @param <V> type of map values ! * @param m the map for which a dynamically typesafe view is to be ! * returned ! * @param keyType the type of key that {@code m} is permitted to hold ! * @param valueType the type of value that {@code m} is permitted to hold ! * @return a dynamically typesafe view of the specified map ! * @since 1.8 ! */ ! public static <K,V> NavigableMap<K,V> checkedNavigableMap(NavigableMap<K, V> m, ! Class<K> keyType, ! Class<V> valueType) { ! return new CheckedNavigableMap<>(m, keyType, valueType); ! } ! ! /** ! * @serial include ! */ ! static class CheckedNavigableMap<K,V> extends CheckedSortedMap<K,V> ! implements NavigableMap<K,V>, Serializable ! { ! private static final long serialVersionUID = -4852462692372534096L; ! ! private final NavigableMap<K, V> nm; ! ! CheckedNavigableMap(NavigableMap<K, V> m, ! Class<K> keyType, Class<V> valueType) { ! super(m, keyType, valueType); ! nm = m; ! } ! ! public Comparator<? super K> comparator() { return nm.comparator(); } ! public K firstKey() { return nm.firstKey(); } ! public K lastKey() { return nm.lastKey(); } ! ! public NavigableMap<K,V> subMap(K fromKey, K toKey) { ! return checkedNavigableMap( ! (NavigableMap<K,V>) ! nm.subMap(fromKey, toKey), keyType, valueType); ! } ! public NavigableMap<K,V> headMap(K toKey) { ! return checkedNavigableMap( ! (NavigableMap<K,V>) ! nm.headMap(toKey), keyType, valueType); ! } ! public NavigableMap<K,V> tailMap(K fromKey) { ! return checkedNavigableMap( ! (NavigableMap<K,V>) ! nm.tailMap(fromKey), keyType, valueType); ! } ! ! public Entry<K, V> lowerEntry(K key) { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lowerEntry(key), valueType); ! } ! ! public K lowerKey(K key) { return nm.lowerKey(key); } ! ! public Entry<K, V> floorEntry(K key) { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lowerEntry(key), valueType); ! } ! ! public K floorKey(K key) { return nm.floorKey(key); } ! ! public Entry<K, V> ceilingEntry(K key) { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.ceilingEntry(key), valueType); ! } ! ! public K ceilingKey(K key) { return nm.ceilingKey(key); } ! ! public Entry<K, V> higherEntry(K key) { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.higherEntry(key), valueType); ! } ! ! public K higherKey(K key) { return nm.higherKey(key); } ! ! public Entry<K, V> firstEntry() { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.firstEntry(), valueType); ! } ! ! public Entry<K, V> lastEntry() { ! return new CheckedMap.CheckedEntrySet.CheckedEntry(nm.lastEntry(), valueType); ! } ! ! public Entry<K, V> pollFirstEntry() { ! Entry<K,V> entry = nm.pollFirstEntry(); ! return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); ! } ! ! public Entry<K, V> pollLastEntry() { ! Entry<K,V> entry = nm.pollLastEntry(); ! return (null == entry) ? null : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); ! } ! ! public NavigableMap<K, V> descendingMap() { ! return checkedNavigableMap(nm.descendingMap(), keyType, valueType); ! } ! ! public NavigableSet<K> navigableKeySet() { ! return checkedNavigableSet(nm.navigableKeySet(), keyType); ! } ! ! public NavigableSet<K> descendingKeySet() { ! return checkedNavigableSet(nm.descendingKeySet(), keyType); ! } ! ! public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { ! return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType); ! } ! ! public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { ! return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType); ! } ! ! public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { ! return checkedNavigableMap(nm.tailMap(fromKey, inclusive), keyType, valueType); ! } ! } ! ! // Empty collections ! ! /** ! * Returns an iterator that has no elements. More precisely, ! * ! * <ul compact> ! * * <li>{@link Iterator#hasNext hasNext} always returns {@code * false}. * * <li>{@link Iterator#next next} always throws {@link * NoSuchElementException}.
*** 3465,3479 **** * * <p>This example illustrates the type-safe way to obtain an empty set: * <pre> * Set&lt;String&gt; s = Collections.emptySet(); * </pre> ! * Implementation note: Implementations of this method need not ! * create a separate <tt>Set</tt> object for each call. Using this ! * method is likely to have comparable cost to using the like-named ! * field. (Unlike this method, the field does not provide type safety.) * * @see #EMPTY_SET * @since 1.5 */ @SuppressWarnings("unchecked") public static final <T> Set<T> emptySet() { --- 4061,4076 ---- * * <p>This example illustrates the type-safe way to obtain an empty set: * <pre> * Set&lt;String&gt; s = Collections.emptySet(); * </pre> ! * @implNote Implementations of this method need not create a separate ! * {@code Set} object for each call. Using this method is likely to have ! * comparable cost to using the like-named field. (Unlike this method, the ! * field does not provide type safety.) * + * @return the empty set * @see #EMPTY_SET * @since 1.5 */ @SuppressWarnings("unchecked") public static final <T> Set<T> emptySet() {
*** 3523,3647 **** return EMPTY_SET; } } /** ! * Returns the empty sorted set (immutable). This set is serializable. * ! * <p>This example illustrates the type-safe way to obtain an empty sorted ! * set: ! * <pre> ! * SortedSet&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. * * @since 1.8 */ @SuppressWarnings("unchecked") ! public static final <E> SortedSet<E> emptySortedSet() { ! return (SortedSet<E>) new EmptySortedSet<>(); } /** ! * @serial include */ - private static class EmptySortedSet<E> - extends AbstractSet<E> - implements SortedSet<E>, Serializable - { - private static final long serialVersionUID = 6316515401502265487L; - public Iterator<E> iterator() { return emptyIterator(); } - public int size() {return 0;} - public boolean isEmpty() {return true;} - public boolean contains(Object obj) {return false;} - public boolean containsAll(Collection<?> c) { return c.isEmpty(); } - public Object[] toArray() { return new Object[0]; } - - public <E> E[] toArray(E[] a) { - if (a.length > 0) - a[0] = null; - return a; - } - - // Preserves singleton property - private Object readResolve() { - return new EmptySortedSet<>(); - } - - @Override - public Comparator<? super E> comparator() { - return null; - } - - @Override @SuppressWarnings("unchecked") ! public SortedSet<E> subSet(Object fromElement, Object toElement) { ! Objects.requireNonNull(fromElement); ! Objects.requireNonNull(toElement); ! ! if (!(fromElement instanceof Comparable) || ! !(toElement instanceof Comparable)) ! { ! throw new ClassCastException(); ! } ! ! if ((((Comparable)fromElement).compareTo(toElement) >= 0) || ! (((Comparable)toElement).compareTo(fromElement) < 0)) ! { ! throw new IllegalArgumentException(); ! } ! ! return emptySortedSet(); ! } ! ! @Override ! public SortedSet<E> headSet(Object toElement) { ! Objects.requireNonNull(toElement); ! ! if (!(toElement instanceof Comparable)) { ! throw new ClassCastException(); ! } ! ! return emptySortedSet(); ! } ! ! @Override ! public SortedSet<E> tailSet(Object fromElement) { ! Objects.requireNonNull(fromElement); ! ! if (!(fromElement instanceof Comparable)) { ! throw new ClassCastException(); ! } ! ! return emptySortedSet(); ! } ! ! @Override ! public E first() { ! throw new NoSuchElementException(); ! } ! ! @Override ! public E last() { ! throw new NoSuchElementException(); ! } ! ! // Override default methods in Collection ! @Override ! public void forEach(Consumer<? super E> action) { ! Objects.requireNonNull(action); ! } ! ! @Override ! public boolean removeIf(Predicate<? super E> filter) { ! Objects.requireNonNull(filter); ! return false; ! } ! ! @Override ! public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); } } /** * The empty list (immutable). This list is serializable. * --- 4120,4168 ---- return EMPTY_SET; } } /** ! * Returns an empty sorted set (immutable). This set is serializable. * ! * <p>This example illustrates the type-safe way to obtain an empty ! * sorted set: ! * <pre> {@code ! * SortedSet<String> s = Collections.emptySortedSet(); ! * }</pre> * + * @implNote Implementations of this method need not create a separate + * {@code SortedSet} object for each call. + * + * @param <E> type of elements, if there were any, in the set + * @return the empty sorted set * @since 1.8 */ @SuppressWarnings("unchecked") ! public static <E> SortedSet<E> emptySortedSet() { ! return (SortedSet<E>) EmptyNavigableSet.EMPTY_NAVIGABLE_SET; } /** ! * Returns an empty navigable set (immutable). This set is serializable. ! * ! * <p>This example illustrates the type-safe way to obtain an empty ! * navigable set: ! * <pre> {@code ! * NavigableSet<String> s = Collections.emptyNavigableSet(); ! * }</pre> ! * ! * @implNote Implementations of this method need not ! * create a separate {@code NavigableSet} object for each call. ! * ! * @param <E> type of elements, if there were any, in the set ! * @return the empty navigable set ! * @since 1.8 */ @SuppressWarnings("unchecked") ! public static <E> NavigableSet<E> emptyNavigableSet() { ! return (NavigableSet<E>) EmptyNavigableSet.EMPTY_NAVIGABLE_SET; } /** * The empty list (immutable). This list is serializable. *
*** 3737,3746 **** --- 4258,4573 ---- return EMPTY_LIST; } } /** + * An empty navigable map with enforced bounds upon the key set. The bounds + * are generated via the various sub-map operations and enforced on + * subsequent sub-map operations. + * + * @serial include + * + * @param <K> type of keys, if there were any, and bounds + * @param <V> type of values, if there were any + */ + private static class BoundedEmptyNavigableMap<K,V> extends EmptyNavigableMap<K,V> + implements Serializable { + + private static final long serialVersionUID = 5065418537829651507L; + + /** + * Our bounded keyset. + */ + final BoundedEmptyNavigableSet keySet; + + private BoundedEmptyNavigableMap(BoundedEmptyNavigableSet keySet) { + this.keySet = Objects.requireNonNull(keySet); + } + + public Comparator<? super K> comparator() + { return keySet.comparator(); } + + @Override + public NavigableMap<K, V> descendingMap() { + NavigableSet<K> descending = keySet.descendingSet(); + + if(descending == Collections.emptyNavigableSet()) { + return Collections.emptyNavigableMap(); + } else { + return new BoundedEmptyNavigableMap((BoundedEmptyNavigableSet<K>) descending); + } + } + + public BoundedEmptyNavigableSet<K> navigableKeySet() { return keySet; } + public EmptyNavigableSet<K> descendingKeySet() + { return keySet.descendingSet(); } + + @Override + public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new BoundedEmptyNavigableMap(keySet.subSet( + fromKey, fromInclusive, + toKey, toInclusive)); + } + + @Override + public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { + return new BoundedEmptyNavigableMap( + keySet.headSet(toKey, inclusive)); + } + + @Override + public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { + return new BoundedEmptyNavigableMap( + keySet.tailSet(fromKey, inclusive)); + } + } + + /** + * @serial include + * + * @param <E> type of elements, if there were any + */ + private static class EmptyNavigableSet<E> extends EmptySet<E> + implements NavigableSet<E>, Serializable { + private static final long serialVersionUID = -6291252904449939134L; + + @SuppressWarnings("rawtypes") + private static final NavigableSet EMPTY_NAVIGABLE_SET = + new EmptyNavigableSet(); + + EmptyNavigableSet() { } + + @Override + public boolean contains(Object obj) { + if( !(Objects.requireNonNull(obj) instanceof Comparable)) { + throw new ClassCastException("Not Comparable"); + } + + return false; + } + + // Preserves singleton property + private Object readResolve() + { return Collections.emptyNavigableSet(); } + public E lower(E e) { return null; } + public E floor(E e) { return null; } + public E ceiling(E e) { return null; } + public E higher(E e) { return null; } + public E pollFirst() { throw new UnsupportedOperationException(); } + public E pollLast() { throw new UnsupportedOperationException(); } + + public EmptyNavigableSet<E> descendingSet() { + return new BoundedEmptyNavigableSet(null, false, null, false, true); + } + + public Iterator<E> descendingIterator() + { return Collections.emptyIterator(); } + + public BoundedEmptyNavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new BoundedEmptyNavigableSet<>( + checkComparable(fromElement), fromInclusive, + checkComparable(toElement), toInclusive, + false); + } + + public BoundedEmptyNavigableSet<E> headSet(E toElement, boolean inclusive) { + return new BoundedEmptyNavigableSet<>( + null, false, + checkComparable(toElement), inclusive, + false); + } + + public BoundedEmptyNavigableSet<E> tailSet(E fromElement, boolean inclusive) { + return new BoundedEmptyNavigableSet<>( + checkComparable(fromElement), inclusive, + null, false, + false); + } + + public final SortedSet<E> subSet(E fromElement, E toElement) + { return subSet(fromElement, true, toElement, false); } + public final SortedSet<E> headSet(E toElement) + { return headSet(toElement, false); } + public final SortedSet<E> tailSet(E fromElement) + { return tailSet(fromElement, true); } + + public Comparator<? super E> comparator() { return null; } + public E first() { throw new NoSuchElementException(); } + public E last() { throw new NoSuchElementException(); } + + /** + * Ensures that the specified object is non-null and Comparable. + * @param <E> type of references + * @param obj the instance to check + * @return the reference as a Comparable + */ + static <E> Comparable<E> checkComparable(E obj) { + return (Comparable<E>) Objects.requireNonNull(obj); + } + } + + /** + * An empty NavigableSet but bounds are maintained. If you try to sub-set + * outside the bounds you will get an IllegalArgumentException. + * + * @serial include + * + * @param <E> type of elements, if there were any, and bounds + */ + private static class BoundedEmptyNavigableSet<E> extends EmptyNavigableSet<E> + implements Serializable { + private static final long serialVersionUID = 3393358742248855583L; + + /** + * {@code true} if lowerBound is "greater" than upperBound. + */ + final boolean descending; + /** + * {@code true} if lowerBound is inclusive. + */ + final boolean lowerInclusive; + /** + * {@code true} if upperBound is inclusive. + */ + final boolean upperInclusive; + /** + * The lower bound of the set. + */ + final Comparable<E> lowerBound; + /** + * The upper bound of the set. + */ + final Comparable<E> upperBound; + + public BoundedEmptyNavigableSet( + Comparable<E> fromElement, boolean lowerInclusive, + Comparable<E> toElement, boolean upperInclusive, + boolean descending) { + this.descending = descending; + + if ((fromElement != null) && (toElement != null)) { + // both bounds are present we need to ensure that they make + // sense. + int fromCompared = Integer.signum(fromElement.compareTo((E)toElement)); + int toCompared = Integer.signum(toElement.compareTo((E)fromElement)); + + if(fromCompared != -toCompared) { + throw new IllegalArgumentException("inconsistent compareTo"); + } + + if (descending) { + if (fromCompared < 0) { + throw new IllegalArgumentException(); + } + } else { + if (fromCompared > 0) { + throw new IllegalArgumentException(); + } + } + } + + this.lowerBound = fromElement; + this.lowerInclusive = lowerInclusive; + this.upperBound = toElement; + this.upperInclusive = upperInclusive; + } + + @Override + public Comparator<? super E> comparator() + { return descending ? Collections.reverseOrder() : null; } + + private Comparable<E> inBounds(E element) { + if (!descending) { + if (null != lowerBound) { + if (lowerInclusive) { + if (lowerBound.compareTo(element) > 0) { + throw new IllegalArgumentException("out of bounds"); + } + } else { + if (lowerBound.compareTo(element) >= 0) { + throw new IllegalArgumentException("out of bounds"); + } + } + } + + if (null != upperBound) { + if (upperInclusive) { + if (upperBound.compareTo(element) < 0) { + throw new IllegalArgumentException("out of bounds"); + } + } else { + if (upperBound.compareTo(element) <= 0) { + throw new IllegalArgumentException("out of bounds"); + } + } + } + } else { + if (null != lowerBound) { + if (lowerInclusive) { + if (lowerBound.compareTo(element) < 0) { + throw new IllegalArgumentException("out of bounds"); + } + } else { + if (lowerBound.compareTo(element) <= 0) { + throw new IllegalArgumentException("out of bounds"); + } + } + } + + if (null != upperBound) { + if (upperInclusive) { + if (upperBound.compareTo(element) > 0) { + throw new IllegalArgumentException("out of bounds"); + } + } else { + if (upperBound.compareTo(element) >= 0) { + throw new IllegalArgumentException("out of bounds"); + } + } + } + } + + return EmptyNavigableSet.checkComparable(element); + } + + @Override + public EmptyNavigableSet<E> descendingSet() { + if (upperBound == null && lowerBound == null && descending) { + return (EmptyNavigableSet) Collections.emptyNavigableSet(); + } + return new BoundedEmptyNavigableSet( + upperBound, upperInclusive, + lowerBound, lowerInclusive, + !descending); + } + + @Override + public BoundedEmptyNavigableSet<E> subSet(E fromKey, boolean fromInclusive, E toKey, boolean toInclusive) { + return new BoundedEmptyNavigableSet( + inBounds(fromKey), fromInclusive, + inBounds(toKey), toInclusive, + descending); + } + + @Override + public BoundedEmptyNavigableSet<E> headSet(E toKey, boolean inclusive) { + return new BoundedEmptyNavigableSet( + lowerBound, lowerInclusive, + inBounds(Objects.requireNonNull(toKey)), inclusive, + descending); + } + + @Override + public BoundedEmptyNavigableSet<E> tailSet(E fromKey, boolean inclusive) { + return new BoundedEmptyNavigableSet( + inBounds(Objects.requireNonNull(fromKey)), inclusive, + upperBound, upperInclusive, + descending); + } + } + + /** * The empty map (immutable). This map is serializable. * * @see #emptyMap() * @since 1.3 */
*** 3748,3775 **** public static final Map EMPTY_MAP = new EmptyMap<>(); /** * Returns the empty map (immutable). This map is serializable. * ! * <p>This example illustrates the type-safe way to obtain an empty set: * <pre> * Map&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 ! * method is likely to have comparable cost to using the like-named ! * field. (Unlike this method, the field does not provide type safety.) * * @see #EMPTY_MAP * @since 1.5 */ @SuppressWarnings("unchecked") public static final <K,V> Map<K,V> emptyMap() { return (Map<K,V>) EMPTY_MAP; } /** * @serial include */ private static class EmptyMap<K,V> extends AbstractMap<K,V> implements Serializable --- 4575,4641 ---- public static final Map EMPTY_MAP = new EmptyMap<>(); /** * Returns the empty map (immutable). This map is serializable. * ! * <p>This example illustrates the type-safe way to obtain an empty map: * <pre> * Map&lt;String, Date&gt; s = Collections.emptyMap(); * </pre> ! * @implNote Implementations of this method need not create a separate ! * {@code Map} object for each call. Using this method is likely to have ! * comparable cost to using the like-named field. (Unlike this method, the ! * field does not provide type safety.) * + * @return an empty map * @see #EMPTY_MAP * @since 1.5 */ @SuppressWarnings("unchecked") public static final <K,V> Map<K,V> emptyMap() { return (Map<K,V>) EMPTY_MAP; } /** + * Returns an empty sorted map (immutable). This map is serializable. + * + * <p>This example illustrates the type-safe way to obtain an empty map: + * <pre> {@code + * SortedMap<String, Date> s = Collections.emptySortedMap(); + * }</pre> + * + * @implNote Implementations of this method need not create a separate + * {@code SortedMap} object for each call. + * + * @return an empty sorted map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final <K,V> SortedMap<K,V> emptySortedMap() { + return (SortedMap<K,V>) EmptyNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** + * Returns an empty navigable map (immutable). This map is serializable. + * + * <p>This example illustrates the type-safe way to obtain an empty map: + * <pre> {@code + * NavigableMap<String, Date> s = Collections.emptyNavigableMap(); + * }</pre> + * + * @implNote Implementations of this method need not create a separate + * {@code NavigableMap} object for each call. + * + * @return an empty navigable map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final <K,V> NavigableMap<K,V> emptyNavigableMap() { + return (NavigableMap<K,V>) EmptyNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** * @serial include */ private static class EmptyMap<K,V> extends AbstractMap<K,V> implements Serializable
*** 3856,3865 **** --- 4722,4863 ---- private Object readResolve() { return EMPTY_MAP; } } + /** + * An empty navigable map. + * + * @param <K> type of keys, if there were any in this map + * @param <V> type of values, if there were any in this map + * + * @serial include + */ + private static class EmptyNavigableMap<K,V> extends EmptyMap<K,V> + implements NavigableMap<K,V>, Serializable { + private static final long serialVersionUID = -2239321462712562324L; + + @SuppressWarnings("rawtypes") + private static final NavigableMap EMPTY_NAVIGABLE_MAP = + new EmptyNavigableMap(); + + EmptyNavigableMap() {} + + @Override + public boolean containsKey(Object obj) { + checkComparable(obj); + return false; + } + + // Preserves singleton property + private Object readResolve() { return Collections.emptyNavigableMap();} + + public final K firstKey() { throw new NoSuchElementException(); } + public final K lastKey() { throw new NoSuchElementException(); } + + public Entry<K, V> lowerEntry(K key) { + checkComparable(key); + return null; + } + + public K lowerKey(K key) { + checkComparable(key); + return null; + } + + public Entry<K, V> floorEntry(K key) { + checkComparable(key); + return null; + } + + public K floorKey(K key) { + checkComparable(key); + return null; + } + + public Entry<K, V> ceilingEntry(K key) { + checkComparable(key); + return null; + } + + public K ceilingKey(K key) { + checkComparable(key); + return null; + } + + public Entry<K, V> higherEntry(K key) { + checkComparable(key); + return null; + } + + public K higherKey(K key) { + checkComparable(key); + return null; + } + + public Entry<K, V> firstEntry() { return null; } + public Entry<K, V> lastEntry() { return null; } + public Entry<K, V> pollFirstEntry() + { throw new UnsupportedOperationException(); } + public Entry<K, V> pollLastEntry() + { throw new UnsupportedOperationException(); } + + public NavigableMap<K, V> descendingMap() { + EmptyNavigableSet<K> descendingKeys = descendingKeySet(); + + if(descendingKeys == emptyNavigableSet()) { + return emptyNavigableMap(); + } else { + return new BoundedEmptyNavigableMap<>((BoundedEmptyNavigableSet<K>) descendingKeys); + } + } + + public EmptyNavigableSet<K> navigableKeySet() + { return (EmptyNavigableSet<K>) Collections.emptyNavigableSet(); } + public EmptyNavigableSet<K> descendingKeySet() { + return navigableKeySet().descendingSet(); + } + + public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet<K>) Collections.emptyNavigableSet()).subSet( + fromKey, fromInclusive, + toKey, toInclusive)); + } + + public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet<K>) Collections.emptyNavigableSet()) + .headSet(toKey, inclusive)); + } + + public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { + return new BoundedEmptyNavigableMap<>( + ((EmptyNavigableSet<K>) Collections.emptyNavigableSet()) + .tailSet(fromKey, inclusive)); + } + + public final SortedMap<K, V> subMap(K fromKey, K toKey) + { return subMap(fromKey, true, toKey, false); } + public final SortedMap<K, V> headMap(K toKey) + { return headMap(toKey, false); } + public final SortedMap<K, V> tailMap(K fromKey) + { return tailMap(fromKey, true); } + public Comparator<? super K> comparator() { return null; } + + /** + * Ensures that the specified object is non-null and Comparable. + * @param <E> type of references + * @param obj the instance to check + * @return the reference as a Comparable + */ + static <E> Comparable<E> checkComparable(E obj) { + return (Comparable<E>) Objects.requireNonNull(obj); + } + + } + // Singleton collections /** * Returns an immutable set containing only the specified object. * The returned set is serializable.
*** 4070,4086 **** k = key; v = value; } public int size() {return 1;} - public boolean isEmpty() {return false;} - public boolean containsKey(Object key) {return eq(key, k);} - public boolean containsValue(Object value) {return eq(value, v);} - public V get(Object key) {return (eq(key, k) ? v : null);} private transient Set<K> keySet = null; private transient Set<Map.Entry<K,V>> entrySet = null; private transient Collection<V> values = null; --- 5068,5080 ----
*** 4414,4423 **** --- 5408,5419 ---- return l; } /** * Returns true if the specified arguments are equal, or both null. + * + * NB: Do not replace with Object.equals until JDK-8015417 is resolved. */ static boolean eq(Object o1, Object o2) { return o1==null ? o2==null : o1.equals(o2); }
*** 4659,4668 **** --- 5655,5782 ---- s = m.keySet(); } } /** + * Returns a navigable set backed by the specified navigable 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 NavigableSet} implementation + * (such as {@link TreeMap}). + * + * <p>Each method invocation on the set returned by this method results in + * exactly one method invocation on the backing map or its {@code keySet} + * view, with one exception. The {@code addAll} method is implemented + * as a sequence of {@code put} invocations on the backing map. + * + * <p>The specified map must be empty at the time this method is invoked, + * and should not be accessed directly after this method returns. These + * conditions are ensured if the map is created empty, passed directly + * to this method, and no reference to the map is retained, as illustrated + * in the following code fragment: + * <pre> {@code + * Set<Object> weakHashSet = Collections.newNavigableSetFromNavigableMap( + * new WeakHashMap<Object, Boolean>()); + * }</pre> + * + * @param map the backing navigable map + * @return the navigable set backed by the map + * @throws IllegalArgumentException if {@code map} is not empty + * @throws NullPointerException if {@code map} is null + * @since 1.8 + */ + public static <E> NavigableSet<E> newNavigableSetFromNavigableMap(NavigableMap<E, Boolean> map) { + return new NavigableSetFromNavigableMap<>(map); + } + + /** + * @serial include + */ + private static class NavigableSetFromNavigableMap<E> extends AbstractSet<E> + implements NavigableSet<E>, Serializable + { + private static final long serialVersionUID = -7303807726339382839L; + + private final NavigableMap<E, Boolean> m; // The backing map + private transient NavigableSet<E> s; // Its keySet + + NavigableSetFromNavigableMap(NavigableMap<E, Boolean> map) { + if (!map.isEmpty()) + throw new IllegalArgumentException("Map is non-empty"); + m = map; + s = map.navigableKeySet(); + } + + public void clear() { m.clear(); } + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean contains(Object o) { return m.containsKey(o); } + public boolean remove(Object o) { return m.remove(o) != null; } + public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; } + public Iterator<E> iterator() { return s.iterator(); } + public Object[] toArray() { return s.toArray(); } + public <T> T[] toArray(T[] a) { return s.toArray(a); } + public String toString() { return s.toString(); } + public int hashCode() { return s.hashCode(); } + public boolean equals(Object o) { return o == this || s.equals(o); } + public boolean containsAll(Collection<?> c) {return s.containsAll(c);} + public boolean removeAll(Collection<?> c) {return s.removeAll(c);} + public boolean retainAll(Collection<?> c) {return s.retainAll(c);} + // addAll is the only inherited implementation + + // Override default methods in Collection + @Override + public void forEach(Consumer<? super E> action) { s.forEach(action); } + @Override + public boolean removeIf(Predicate<? super E> filter) { + return s.removeIf(filter); + } + + @Override + public Spliterator<E> spliterator() {return s.spliterator();} + + private void readObject(java.io.ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (null == m) { + throw new InvalidObjectException("null map"); + } + s = m.navigableKeySet(); + } + + public E lower(E e) { return m.lowerKey(e); } + public E floor(E e) { return m.floorKey(e); } + public E ceiling(E e) { return m.ceilingKey(e); } + public E higher(E e) { return m.higherKey(e); } + public E pollFirst() { return s.pollFirst(); } + public E pollLast() { return s.pollLast(); } + public NavigableSet<E> descendingSet() { return s.descendingSet(); } + public Iterator<E> descendingIterator(){return s.descendingIterator();} + + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return s.subSet(fromElement, fromInclusive, toElement, toInclusive); + } + + public NavigableSet<E> headSet(E toElement, boolean inclusive) + { return s.headSet(toElement, inclusive); } + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) + { return s.tailSet(fromElement, inclusive); } + public SortedSet<E> subSet(E fromElement, E toElement) + { return s.subSet(fromElement, toElement); } + public SortedSet<E> headSet(E toElement) + { return s.headSet(toElement); } + public SortedSet<E> tailSet(E fromElement) + { return s.tailSet(fromElement); } + public Comparator<? super E> comparator() { return s.comparator(); } + public E first() { return s.first(); } + public E last() { return s.last(); } + } + + /** * Returns a view of a {@link Deque} as a Last-in-first-out (Lifo) * {@link Queue}. Method <tt>add</tt> is mapped to <tt>push</tt>, * <tt>remove</tt> is mapped to <tt>pop</tt> and so on. This * view can be useful when you would like to use a method * requiring a <tt>Queue</tt> but you need Lifo ordering.