diff --git a/src/share/classes/java/util/AbstractMap.java b/src/share/classes/java/util/AbstractMap.java index fdca2aa79931f50363fdf413b8a547ffe4fecbd2..7c5153fc3bb98d64b274e23208a3adf1f727aef2 100644 --- a/src/share/classes/java/util/AbstractMap.java +++ b/src/share/classes/java/util/AbstractMap.java @@ -543,6 +543,8 @@ public abstract class AbstractMap implements Map { /** * Utility method for SimpleEntry and SimpleImmutableEntry. * Test for equality, checking for nulls. + * + * NB: Do not replace with Object.equals until JDK-8015417 is resolved. */ private static boolean eq(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); diff --git a/src/share/classes/java/util/Collections.java b/src/share/classes/java/util/Collections.java index 56f16f6809bfacfff63045db32b9d428b939f2f1..bbb987ec58fb93be6e8a8a48019b059ce5965d49 100644 --- a/src/share/classes/java/util/Collections.java +++ b/src/share/classes/java/util/Collections.java @@ -27,6 +27,7 @@ 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; @@ -138,7 +139,7 @@ public class Collections { * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. @@ -199,7 +200,7 @@ public class Collections { * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. @@ -1213,6 +1214,94 @@ public class Collections { public E last() {return ss.last();} } + /** + * Returns an unmodifiable view of the specified navigable set. This method + * allows modules to provide users with "read-only" access to internal + * navigable sets. Query operations on the returned navigable set "read + * through" to the specified navigable set. Attempts to modify the returned + * navigable set, whether direct, via its iterator, or via its + * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in + * an {@code UnsupportedOperationException}.

+ * + * 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 NavigableSet unmodifiableNavigableSet(NavigableSet s) { + return new UnmodifiableNavigableSet<>(s); + } + + /** + * Wraps a navigable set and disables all of the mutative operations. + * + * @param type of elements + * @serial include + */ + static class UnmodifiableNavigableSet + extends UnmodifiableSortedSet + implements NavigableSet, Serializable { + + private static final long serialVersionUID = -6027448201786391929L; + + /** + * A singleton empty unmodifiable navigable set used for + * {@link #emptyNavigableSet()}. + * + * @param type of elements, if there were any, and bounds + */ + private static class EmptyNavigableSet extends UnmodifiableNavigableSet + implements Serializable { + private static final long serialVersionUID = -6291252904449939134L; + + public EmptyNavigableSet() { + super(new TreeSet()); + } + + private Object readResolve() { return EMPTY_NAVIGABLE_SET; } + } + + @SuppressWarnings("rawtypes") + private static final NavigableSet EMPTY_NAVIGABLE_SET = + new EmptyNavigableSet<>(); + + /** + * The instance we are protecting. + */ + private final NavigableSet ns; + + UnmodifiableNavigableSet(NavigableSet s) {super(s); ns = s;} + + public E lower(E e) { return ns.lower(e); } + public E floor(E e) { return ns.floor(e); } + public E ceiling(E e) { return ns.ceiling(e); } + public E higher(E e) { return ns.higher(e); } + public E pollFirst() { throw new UnsupportedOperationException(); } + public E pollLast() { throw new UnsupportedOperationException(); } + public NavigableSet descendingSet() + { return new UnmodifiableNavigableSet<>(ns.descendingSet()); } + public Iterator descendingIterator() + { return descendingSet().iterator(); } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new UnmodifiableNavigableSet<>( + ns.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>( + ns.headSet(toElement, inclusive)); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>( + ns.tailSet(fromElement, inclusive)); + } + } + /** * Returns an unmodifiable view of the specified list. This method allows * modules to provide users with "read-only" access to internal @@ -1240,6 +1329,7 @@ public class Collections { static class UnmodifiableList extends UnmodifiableCollection implements List { private static final long serialVersionUID = -283967356065247728L; + final List list; UnmodifiableList(List list) { @@ -1682,7 +1772,8 @@ public class Collections { private static class UnmodifiableEntry implements Map.Entry { private Map.Entry e; - UnmodifiableEntry(Map.Entry e) {this.e = e;} + UnmodifiableEntry(Map.Entry e) + {this.e = Objects.requireNonNull(e);} public K getKey() {return e.getKey();} public V getValue() {return e.getValue();} @@ -1734,24 +1825,151 @@ public class Collections { private final SortedMap sm; - UnmodifiableSortedMap(SortedMap m) {super(m); sm = m;} + UnmodifiableSortedMap(SortedMap m) {super(m); sm = m; } + public Comparator comparator() { return sm.comparator(); } + public SortedMap subMap(K fromKey, K toKey) + { return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); } + public SortedMap headMap(K toKey) + { return new UnmodifiableSortedMap<>(sm.headMap(toKey)); } + public SortedMap tailMap(K fromKey) + { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); } + public K firstKey() { return sm.firstKey(); } + public K lastKey() { return sm.lastKey(); } + } - public Comparator comparator() {return sm.comparator();} + /** + * 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}.

+ * + * The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param m the navigable map for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable map + * @since 1.8 + */ + public static NavigableMap unmodifiableNavigableMap(NavigableMap m) { + return new UnmodifiableNavigableMap<>(m); + } - public SortedMap subMap(K fromKey, K toKey) { - return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); + /** + * @serial include + */ + static class UnmodifiableNavigableMap + extends UnmodifiableSortedMap + implements NavigableMap, Serializable { + private static final long serialVersionUID = -4858195264774772197L; + + /** + * A class for the {@link EMPTY_NAVIGABLE_MAP} which needs readResolve + * to preserve singleton property. + * + * @param type of keys, if there were any, and of bounds + * @param type of values, if there were any + */ + private static class EmptyNavigableMap extends UnmodifiableNavigableMap + implements Serializable { + + private static final long serialVersionUID = -2239321462712562324L; + + EmptyNavigableMap() { super(new TreeMap()); } + + @Override + public NavigableSet navigableKeySet() + { return emptyNavigableSet(); } + + private Object readResolve() { return EMPTY_NAVIGABLE_MAP; } } - public SortedMap headMap(K toKey) { - return new UnmodifiableSortedMap<>(sm.headMap(toKey)); + + /** + * Singleton for {@link emptyNavigableMap()} which is also immutable. + */ + private static final EmptyNavigableMap EMPTY_NAVIGABLE_MAP = + new EmptyNavigableMap<>(); + + /** + * The instance we wrap and protect. + */ + private final NavigableMap nm; + + UnmodifiableNavigableMap(NavigableMap m) + {super(m); nm = m;} + + public K lowerKey(K key) { return nm.lowerKey(key); } + public K floorKey(K key) { return nm.floorKey(key); } + public K ceilingKey(K key) { return nm.ceilingKey(key); } + public K higherKey(K key) { return nm.higherKey(key); } + + public Entry lowerEntry(K key) { + Entry lower = (Entry) nm.lowerEntry(key); + return (null != lower) + ? new UnmodifiableEntrySet.UnmodifiableEntry(lower) + : null; } - public SortedMap tailMap(K fromKey) { - return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); + + public Entry floorEntry(K key) { + Entry floor = (Entry) nm.floorEntry(key); + return (null != floor) + ? new UnmodifiableEntrySet.UnmodifiableEntry(floor) + : null; } - public K firstKey() {return sm.firstKey();} - public K lastKey() {return sm.lastKey();} - } + public Entry ceilingEntry(K key) { + Entry ceiling = (Entry) nm.ceilingEntry(key); + return (null != ceiling) + ? new UnmodifiableEntrySet.UnmodifiableEntry(ceiling) + : null; + } + + + public Entry higherEntry(K key) { + Entry higher = (Entry) nm.higherEntry(key); + return (null != higher) + ? new UnmodifiableEntrySet.UnmodifiableEntry(higher) + : null; + } + + public Entry firstEntry() { + Entry first = (Entry) nm.firstEntry(); + return (null != first) + ? new UnmodifiableEntrySet.UnmodifiableEntry(first) + : null; + } + + public Entry lastEntry() { + Entry last = (Entry) nm.lastEntry(); + return (null != last) + ? new UnmodifiableEntrySet.UnmodifiableEntry(last) + : null; + } + + public Entry pollFirstEntry() + { throw new UnsupportedOperationException(); } + public Entry pollLastEntry() + { throw new UnsupportedOperationException(); } + public NavigableMap descendingMap() + { return unmodifiableNavigableMap(nm.descendingMap()); } + public NavigableSet navigableKeySet() + { return unmodifiableNavigableSet(nm.navigableKeySet()); } + public NavigableSet descendingKeySet() + { return unmodifiableNavigableSet(nm.descendingKeySet()); } + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return unmodifiableNavigableMap( + nm.subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + public NavigableMap headMap(K toKey, boolean inclusive) + { return unmodifiableNavigableMap(nm.headMap(toKey, inclusive)); } + public NavigableMap tailMap(K fromKey, boolean inclusive) + { return unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive)); } + } // Synch Wrappers @@ -1805,14 +2023,13 @@ public class Collections { final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection c) { - if (c==null) - throw new NullPointerException(); - this.c = c; + this.c = Objects.requireNonNull(c); mutex = this; } + SynchronizedCollection(Collection c, Object mutex) { - this.c = c; - this.mutex = mutex; + this.c = Objects.requireNonNull(c); + this.mutex = Objects.requireNonNull(mutex); } public int size() { @@ -2026,6 +2243,120 @@ public class Collections { } } + /** + * Returns a synchronized (thread-safe) navigable set backed by the + * specified navigable set. In order to guarantee serial access, it is + * critical that all access to the backing navigable set is + * accomplished through the returned navigable set (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * navigable set when iterating over it or any of its {@code subSet}, + * {@code headSet}, or {@code tailSet} views. + *

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

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param s the navigable set to be "wrapped" in a synchronized navigable + * set + * @return a synchronized view of the specified navigable set + * @since 1.8 + */ + public static NavigableSet synchronizedNavigableSet(NavigableSet s) { + return new SynchronizedNavigableSet<>(s); + } + + /** + * @serial include + */ + static class SynchronizedNavigableSet + extends SynchronizedSortedSet + implements NavigableSet + { + private static final long serialVersionUID = -5505529816273629798L; + + private final NavigableSet ns; + + SynchronizedNavigableSet(NavigableSet s) { + super(s); + ns = s; + } + + SynchronizedNavigableSet(NavigableSet s, Object mutex) { + super(s, mutex); + ns = s; + } + public E lower(E e) { synchronized (mutex) {return ns.lower(e);} } + public E floor(E e) { synchronized (mutex) {return ns.floor(e);} } + public E ceiling(E e) { synchronized (mutex) {return ns.ceiling(e);} } + public E higher(E e) { synchronized (mutex) {return ns.higher(e);} } + public E pollFirst() { synchronized (mutex) {return ns.pollFirst();} } + public E pollLast() { synchronized (mutex) {return ns.pollLast();} } + + public NavigableSet descendingSet() { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex); + } + } + + public Iterator descendingIterator() + { synchronized (mutex) { return descendingSet().iterator(); } } + + public NavigableSet subSet(E fromElement, E toElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, true, toElement, false), mutex); + } + } + public NavigableSet headSet(E toElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, false), mutex); + } + } + public NavigableSet tailSet(E fromElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet(ns.tailSet(fromElement, true), mutex); + } + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex); + } + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex); + } + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive)); + } + } + } + /** * Returns a synchronized (thread-safe) list backed by the specified * list. In order to guarantee serial access, it is critical that @@ -2235,9 +2566,7 @@ public class Collections { final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { - if (m==null) - throw new NullPointerException(); - this.m = m; + this.m = Objects.requireNonNull(m); mutex = this; } @@ -2416,7 +2745,6 @@ public class Collections { return new SynchronizedSortedMap<>(m); } - /** * @serial include */ @@ -2466,6 +2794,164 @@ public class Collections { } } + /** + * Returns a synchronized (thread-safe) navigable map backed by the + * specified navigable map. In order to guarantee serial access, it is + * critical that all access to the backing navigable map is + * accomplished through the returned navigable map (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * navigable map when iterating over any of its collection views, or the + * collections views of any of its {@code subMap}, {@code headMap} or + * {@code tailMap} views. + *

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

The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param m the navigable map to be "wrapped" in a synchronized navigable + * map + * @return a synchronized view of the specified navigable map. + * @since 1.8 + */ + public static NavigableMap synchronizedNavigableMap(NavigableMap m) { + return new SynchronizedNavigableMap<>(m); + } + + /** + * A synchronized NavigableMap. + * + * @serial include + */ + static class SynchronizedNavigableMap + extends SynchronizedSortedMap + implements NavigableMap + { + private static final long serialVersionUID = 699392247599746807L; + + private final NavigableMap nm; + + SynchronizedNavigableMap(NavigableMap m) { + super(m); + nm = m; + } + SynchronizedNavigableMap(NavigableMap m, Object mutex) { + super(m, mutex); + nm = m; + } + + public Entry lowerEntry(K key) + { synchronized (mutex) { return nm.lowerEntry(key); } } + public K lowerKey(K key) + { synchronized (mutex) { return nm.lowerKey(key); } } + public Entry floorEntry(K key) + { synchronized (mutex) { return nm.floorEntry(key); } } + public K floorKey(K key) + { synchronized (mutex) { return nm.floorKey(key); } } + public Entry ceilingEntry(K key) + { synchronized (mutex) { return nm.ceilingEntry(key); } } + public K ceilingKey(K key) + { synchronized (mutex) { return nm.ceilingKey(key); } } + public Entry higherEntry(K key) + { synchronized (mutex) { return nm.higherEntry(key); } } + public K higherKey(K key) + { synchronized (mutex) { return nm.higherKey(key); } } + public Entry firstEntry() + { synchronized (mutex) { return nm.firstEntry(); } } + public Entry lastEntry() + { synchronized (mutex) { return nm.lastEntry(); } } + public Entry pollFirstEntry() + { synchronized (mutex) { return nm.pollFirstEntry(); } } + public Entry pollLastEntry() + { synchronized (mutex) { return nm.pollLastEntry(); } } + + public NavigableMap descendingMap() { + synchronized (mutex) { + return + new SynchronizedNavigableMap(nm.descendingMap(), mutex); + } + } + + public NavigableSet keySet() { + return navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex); + } + } + + public NavigableSet descendingKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex); + } + } + + + public SortedMap subMap(K fromKey, K toKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>( + nm.subMap(fromKey, true, toKey, false), mutex); + } + } + public SortedMap headMap(K toKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>(nm.headMap(toKey, false), mutex); + } + } + public SortedMap tailMap(K fromKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>(nm.tailMap(fromKey, true),mutex); + } + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); + } + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.headMap(toKey, inclusive), mutex); + } + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.tailMap(fromKey, inclusive), mutex); + } + } + } + // Dynamically typesafe collection wrappers /** @@ -2497,12 +2983,12 @@ public class Collections { * program to wrap the collection with a dynamically typesafe view. * For example, this declaration: *

 {@code
-     *     Collection c = new HashSet();
+     *     Collection c = new HashSet<>();
      * }
* may be replaced temporarily by this one: *
 {@code
      *     Collection c = Collections.checkedCollection(
-     *         new HashSet(), String.class);
+     *         new HashSet<>(), String.class);
      * }
* Running the program again will cause it to fail at the point where * an incorrectly typed element is inserted into the collection, clearly @@ -2788,6 +3274,7 @@ public class Collections { implements SortedSet, Serializable { private static final long serialVersionUID = 1599911165492914959L; + private final SortedSet ss; CheckedSortedSet(SortedSet s, Class type) { @@ -2810,6 +3297,87 @@ public class Collections { } } +/** + * Returns a dynamically typesafe view of the specified navigable set. + * Any attempt to insert an element of the wrong type will result in an + * immediate {@link ClassCastException}. Assuming a navigable set + * contains no incorrectly typed elements prior to the time a + * dynamically typesafe view is generated, and that all subsequent + * access to the navigable set takes place through the view, it is + * guaranteed that the navigable set cannot contain an incorrectly + * typed element. + * + *

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

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

Since {@code null} is considered to be a value of any reference + * type, the returned navigable set permits insertion of null elements + * whenever the backing sorted set does. + * + * @param s the navigable set for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code s} is permitted to hold + * @return a dynamically typesafe view of the specified navigable set + * @since 1.8 + */ + public static NavigableSet checkedNavigableSet(NavigableSet s, + Class type) { + return new CheckedNavigableSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedNavigableSet extends CheckedSortedSet + implements NavigableSet, Serializable + { + private static final long serialVersionUID = -5429120189805438922L; + + private final NavigableSet ns; + + CheckedNavigableSet(NavigableSet s, Class type) { + super(s, type); + ns = s; + } + + public E lower(E e) { return ns.lower(e); } + public E floor(E e) { return ns.floor(e); } + public E ceiling(E e) { return ns.ceiling(e); } + public E higher(E e) { return ns.higher(e); } + public E pollFirst() { return ns.pollFirst(); } + public E pollLast() {return ns.pollLast(); } + public NavigableSet descendingSet() + { return checkedNavigableSet(ns.descendingSet(), type); } + public Iterator descendingIterator() + {return checkedNavigableSet(ns.descendingSet(), type).iterator(); } + + public NavigableSet subSet(E fromElement, E toElement) { + return checkedNavigableSet(ns.subSet(fromElement, true, toElement, false), type); + } + public NavigableSet headSet(E toElement) { + return checkedNavigableSet(ns.headSet(toElement, false), type); + } + public NavigableSet tailSet(E fromElement) { + return checkedNavigableSet(ns.tailSet(fromElement, true), type); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return checkedNavigableSet(ns.headSet(toElement, inclusive), type); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return checkedNavigableSet(ns.tailSet(fromElement, inclusive), type); + } + } + /** * Returns a dynamically typesafe view of the specified list. * Any attempt to insert an element of the wrong type will result in @@ -3022,11 +3590,9 @@ public class Collections { } CheckedMap(Map m, Class keyType, Class valueType) { - if (m == null || keyType == null || valueType == null) - throw new NullPointerException(); - this.m = m; - this.keyType = keyType; - this.valueType = valueType; + this.m = Objects.requireNonNull(m); + this.keyType = Objects.requireNonNull(keyType); + this.valueType = Objects.requireNonNull(valueType); } public int size() { return m.size(); } @@ -3303,8 +3869,8 @@ public class Collections { private final Class valueType; CheckedEntry(Map.Entry e, Class valueType) { - this.e = e; - this.valueType = valueType; + this.e = Objects.requireNonNull(e); + this.valueType = Objects.requireNonNull(valueType); } public K getKey() { return e.getKey(); } @@ -3407,6 +3973,177 @@ public class Collections { } } + /** + * Returns a dynamically typesafe view of the specified navigable map. + * Any attempt to insert a mapping whose key or value have the wrong + * type will result in an immediate {@link ClassCastException}. + * Similarly, any attempt to modify the value currently associated with + * a key will result in an immediate {@link ClassCastException}, + * whether the modification is attempted directly through the map + * itself, or through a {@link Map.Entry} instance obtained from the + * map's {@link Map#entrySet() entry set} view. + * + *

Assuming a map contains no incorrectly typed keys or values + * prior to the time a dynamically typesafe view is generated, and + * that all subsequent access to the map takes place through the view + * (or one of its collection views), it is guaranteed that the + * map cannot contain an incorrectly typed key or value. + * + *

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

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

Since {@code null} is considered to be a value of any reference + * type, the returned map permits insertion of null keys or values + * whenever the backing map does. + * + * @param type of map keys + * @param type of map values + * @param m the map for which a dynamically typesafe view is to be + * returned + * @param keyType the type of key that {@code m} is permitted to hold + * @param valueType the type of value that {@code m} is permitted to hold + * @return a dynamically typesafe view of the specified map + * @since 1.8 + */ + public static NavigableMap checkedNavigableMap(NavigableMap m, + Class keyType, + Class valueType) { + return new CheckedNavigableMap<>(m, keyType, valueType); + } + + /** + * @serial include + */ + static class CheckedNavigableMap extends CheckedSortedMap + implements NavigableMap, Serializable + { + private static final long serialVersionUID = -4852462692372534096L; + + private final NavigableMap nm; + + CheckedNavigableMap(NavigableMap m, + Class keyType, Class valueType) { + super(m, keyType, valueType); + nm = m; + } + + public Comparator comparator() { return nm.comparator(); } + public K firstKey() { return nm.firstKey(); } + public K lastKey() { return nm.lastKey(); } + + public Entry lowerEntry(K key) { + Entry lower = nm.lowerEntry(key); + return (null != lower) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(lower, valueType) + : null; + } + + public K lowerKey(K key) { return nm.lowerKey(key); } + + public Entry floorEntry(K key) { + Entry floor = nm.floorEntry(key); + return (null != floor) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(floor, valueType) + : null; + } + + public K floorKey(K key) { return nm.floorKey(key); } + + public Entry ceilingEntry(K key) { + Entry ceiling = nm.ceilingEntry(key); + return (null != ceiling) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(ceiling, valueType) + : null; + } + + public K ceilingKey(K key) { return nm.ceilingKey(key); } + + public Entry higherEntry(K key) { + Entry higher = nm.higherEntry(key); + return (null != higher) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(higher, valueType) + : null; + } + + public K higherKey(K key) { return nm.higherKey(key); } + + public Entry firstEntry() { + Entry first = nm.firstEntry(); + return (null != first) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(first, valueType) + : null; + } + + public Entry lastEntry() { + Entry last = nm.lastEntry(); + return (null != last) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(last, valueType) + : null; + } + + public Entry pollFirstEntry() { + Entry entry = nm.pollFirstEntry(); + return (null == entry) + ? null + : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + public Entry pollLastEntry() { + Entry entry = nm.pollLastEntry(); + return (null == entry) + ? null + : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + public NavigableMap descendingMap() { + return checkedNavigableMap(nm.descendingMap(), keyType, valueType); + } + + public NavigableSet keySet() { + return navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + return checkedNavigableSet(nm.navigableKeySet(), keyType); + } + + public NavigableSet descendingKeySet() { + return checkedNavigableSet(nm.descendingKeySet(), keyType); + } + + @Override + public NavigableMap subMap(K fromKey, K toKey) { + return checkedNavigableMap(nm.subMap(fromKey, true, toKey, false), + keyType, valueType); + } + + @Override + public NavigableMap headMap(K toKey) { + return checkedNavigableMap(nm.headMap(toKey, false), keyType, valueType); + } + + @Override + public NavigableMap tailMap(K fromKey) { + return checkedNavigableMap(nm.tailMap(fromKey, true), keyType, valueType); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return checkedNavigableMap(nm.tailMap(fromKey, inclusive), keyType, valueType); + } + } + // Empty collections /** @@ -3428,6 +4165,7 @@ public class Collections { *

Implementations of this method are permitted, but not * required, to return the same object from multiple invocations. * + * @param type of elements, if there were any, in the iterator * @return an empty iterator * @since 1.7 */ @@ -3478,6 +4216,7 @@ public class Collections { *

Implementations of this method are permitted, but not * required, to return the same object from multiple invocations. * + * @param type of elements, if there were any, in the iterator * @return an empty list iterator * @since 1.7 */ @@ -3542,17 +4281,19 @@ public class Collections { public static final Set EMPTY_SET = new EmptySet<>(); /** - * Returns the empty set (immutable). This set is serializable. + * Returns an empty set (immutable). This set is serializable. * Unlike the like-named field, this method is parameterized. * *

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

      *     Set<String> s = Collections.emptySet();
      * 
- * Implementation note: Implementations of this method need not - * create a separate Set object for each call. Using this - * method is likely to have comparable cost to using the like-named - * field. (Unlike this method, the field does not provide type safety.) + * @implNote Implementations of this method need not create a separate + * {@code Set} object for each call. Using this method is likely to have + * comparable cost to using the like-named field. (Unlike this method, the + * field does not provide type safety.) + * + * @return the empty set * * @see #EMPTY_SET * @since 1.5 @@ -3607,121 +4348,45 @@ public class Collections { } /** - * Returns the empty sorted set (immutable). This set is serializable. + * Returns an empty sorted set (immutable). This set is serializable. * - *

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

-     *     SortedSet<String> s = Collections.emptySortedSet();
-     * 
- * Implementation note: Implementations of this method need not - * create a separate SortedSet object for each call. + *

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

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

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

 {@code
+     *     NavigableSet s = Collections.emptyNavigableSet();
+     * }
+ * + * @implNote Implementations of this method need not + * create a separate {@code NavigableSet} object for each call. + * + * @param type of elements, if there were any, in the set + * @return the empty navigable set + * @since 1.8 */ - private static class EmptySortedSet - extends AbstractSet - implements SortedSet, Serializable - { - private static final long serialVersionUID = 6316515401502265487L; - public Iterator iterator() { return emptyIterator(); } - public int size() {return 0;} - public boolean isEmpty() {return true;} - public boolean contains(Object obj) {return false;} - public boolean containsAll(Collection c) { return c.isEmpty(); } - public Object[] toArray() { return new Object[0]; } - - public E[] toArray(E[] a) { - if (a.length > 0) - a[0] = null; - return a; - } - - // Preserves singleton property - private Object readResolve() { - return new EmptySortedSet<>(); - } - - @Override - public Comparator comparator() { - return null; - } - - @Override - @SuppressWarnings("unchecked") - public SortedSet subSet(Object fromElement, Object toElement) { - Objects.requireNonNull(fromElement); - Objects.requireNonNull(toElement); - - if (!(fromElement instanceof Comparable) || - !(toElement instanceof Comparable)) - { - throw new ClassCastException(); - } - - if ((((Comparable)fromElement).compareTo(toElement) >= 0) || - (((Comparable)toElement).compareTo(fromElement) < 0)) - { - throw new IllegalArgumentException(); - } - - return emptySortedSet(); - } - - @Override - public SortedSet headSet(Object toElement) { - Objects.requireNonNull(toElement); - - if (!(toElement instanceof Comparable)) { - throw new ClassCastException(); - } - - return emptySortedSet(); - } - - @Override - public SortedSet tailSet(Object fromElement) { - Objects.requireNonNull(fromElement); - - if (!(fromElement instanceof Comparable)) { - throw new ClassCastException(); - } - - return emptySortedSet(); - } - - @Override - public E first() { - throw new NoSuchElementException(); - } - - @Override - public E last() { - throw new NoSuchElementException(); - } - - // Override default methods in Collection - @Override - public void forEach(Consumer action) { - Objects.requireNonNull(action); - } - - @Override - public boolean removeIf(Predicate filter) { - Objects.requireNonNull(filter); - return false; - } - - @Override - public Spliterator spliterator() { return Spliterators.emptySpliterator(); } + @SuppressWarnings("unchecked") + public static NavigableSet emptyNavigableSet() { + return (NavigableSet) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET; } /** @@ -3733,7 +4398,7 @@ public class Collections { public static final List EMPTY_LIST = new EmptyList<>(); /** - * Returns the empty list (immutable). This list is serializable. + * Returns an empty list (immutable). This list is serializable. * *

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

@@ -3744,6 +4409,9 @@ public class Collections {
      * method is likely to have comparable cost to using the like-named
      * field.  (Unlike this method, the field does not provide type safety.)
      *
+     * @param  type of elements, if there were any, in the list
+     * @return an empty immutable list
+     *
      * @see #EMPTY_LIST
      * @since 1.5
      */
@@ -3830,17 +4498,18 @@ public class Collections {
     public static final Map EMPTY_MAP = new EmptyMap<>();
 
     /**
-     * Returns the empty map (immutable).  This map is serializable.
+     * Returns an empty map (immutable).  This map is serializable.
      *
-     * 

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

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

      *     Map<String, Date> s = Collections.emptyMap();
      * 
- * Implementation note: Implementations of this method need not - * create a separate Map object for each call. Using this - * method is likely to have comparable cost to using the like-named - * field. (Unlike this method, the field does not provide type safety.) + * @implNote Implementations of this method need not create a separate + * {@code Map} object for each call. Using this method is likely to have + * comparable cost to using the like-named field. (Unlike this method, the + * field does not provide type safety.) * + * @return an empty map * @see #EMPTY_MAP * @since 1.5 */ @@ -3849,6 +4518,44 @@ public class Collections { return (Map) EMPTY_MAP; } + /** + * Returns an empty sorted map (immutable). This map is serializable. + * + *

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

 {@code
+     *     SortedMap s = Collections.emptySortedMap();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code SortedMap} object for each call. + * + * @return an empty sorted map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final SortedMap emptySortedMap() { + return (SortedMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** + * Returns an empty navigable map (immutable). This map is serializable. + * + *

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

 {@code
+     *     NavigableMap s = Collections.emptyNavigableMap();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code NavigableMap} object for each call. + * + * @return an empty navigable map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final NavigableMap emptyNavigableMap() { + return (NavigableMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; + } + /** * @serial include */ @@ -4153,15 +4860,11 @@ public class Collections { v = value; } - public int size() {return 1;} - - public boolean isEmpty() {return false;} - - public boolean containsKey(Object key) {return eq(key, k);} - - public boolean containsValue(Object value) {return eq(value, v);} - - public V get(Object key) {return (eq(key, k) ? v : null);} + public int size() {return 1;} + public boolean isEmpty() {return false;} + public boolean containsKey(Object key) {return eq(key, k);} + public boolean containsValue(Object value) {return eq(value, v);} + public V get(Object key) {return (eq(key, k) ? v : null);} private transient Set keySet = null; private transient Set> entrySet = null; @@ -4508,6 +5211,8 @@ public class Collections { /** * 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); diff --git a/src/share/classes/java/util/NavigableSet.java b/src/share/classes/java/util/NavigableSet.java index 26fcd3a7c35fb79166ba186b971553b1a079ea61..4dbb1c1cc454427ee0e7df5ae91e399f821c68e8 100644 --- a/src/share/classes/java/util/NavigableSet.java +++ b/src/share/classes/java/util/NavigableSet.java @@ -303,7 +303,7 @@ public interface NavigableSet extends SortedSet { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} -na */ + */ SortedSet headSet(E toElement); /** diff --git a/src/share/classes/java/util/Optional.java b/src/share/classes/java/util/Optional.java index b51a4d261221383207e353e606c5f7c600770623..4e95e18684b335f7a82986a6f6b186fa9ad6a6fc 100644 --- a/src/share/classes/java/util/Optional.java +++ b/src/share/classes/java/util/Optional.java @@ -25,6 +25,8 @@ package java.util; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; /** @@ -52,7 +54,7 @@ public final class Optional { private final T value; /** - * Construct an empty instance. + * Constructs an empty instance. * * @implNote Generally only one empty instance, {@link Optional#EMPTY}, * should exist per VM. @@ -80,7 +82,7 @@ public final class Optional { } /** - * Construct an instance with the value present. + * Constructs an instance with the value present. * * @param value the non-null value to be present */ @@ -89,7 +91,7 @@ public final class Optional { } /** - * Return an {@code Optional} with the specified present value. + * Returns an {@code Optional} with the specified present non-null value. * * @param value the value to be present, which must be non-null * @return an {@code Optional} with the value present @@ -98,6 +100,18 @@ public final class Optional { return new Optional<>(value); } + /** + * Returns an {@code Optional} describing the specified value, if non-null, + * otherwise returns an empty {@code Optional}. + * + * @param value the possibly-null value to describe + * @return an {@code Optional} with a present value if the specified value + * is non-null, otherwise an empty {@code Optional} + */ + public static Optional ofNullable(T value) { + return value == null ? empty() : of(value); + } + /** * If a value is present in this {@code Optional}, returns the value, * otherwise throws {@code NoSuchElementException}. @@ -124,7 +138,7 @@ public final class Optional { } /** - * Have the specified consumer accept the value if a value is present, + * If a value is present, invoke the specified consumer with the value, * otherwise do nothing. * * @param consumer block to be executed if a value is present @@ -136,6 +150,89 @@ public final class Optional { consumer.accept(value); } + /** + * If a value is present, and the value matches the given predicate, + * return an {@code Optional} describing the value, otherwise return an + * empty {@code Optional}. + * + * @param predicate a predicate to apply to the value, if present + * @return an {@code Optional} describing the value of this {@code Optional} + * if a value is present and the value matches the given predicate, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the predicate is null + */ + public Optional filter(Predicate predicate) { + Objects.requireNonNull(predicate); + if (!isPresent()) + return this; + else + return predicate.test(value) ? this : empty(); + } + + /** + * If a value is present, apply the provided mapping function to it, + * and if the result is non-null, return an {@code Optional} describing the + * result. Otherwise return an empty {@code Optional}. + * + * @apiNote This method supports post-processing on optional values, without + * the need to explicitly check for a return status. For example, the + * following code traverses a stream of file names, selects one that has + * not yet been processed, and then opens that file, returning an + * {@code Optional}: + * + *
{@code
+     *     Optional fis =
+     *         names.stream().filter(name -> !isProcessedYet(name))
+     *                       .findFirst()
+     *                       .map(name -> new FileInputStream(name));
+     * }
+ * + * Here, {@code findFirst} returns an {@code Optional}, and then + * {@code map} returns an {@code Optional} for the desired + * file if one exists. + * + * @param The type of the result of the mapping function + * @param mapper a mapping function to apply to the value, if present + * @return an {@code Optional} describing the result of applying a mapping + * function to the value of this {@code Optional}, if a value is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null + */ + public Optional map(Function mapper) { + Objects.requireNonNull(mapper); + if (!isPresent()) + return empty(); + else { + return Optional.ofNullable(mapper.apply(value)); + } + } + + /** + * If a value is present, apply the provided {@code Optional}-bearing + * mapping function to it, return that result, otherwise return an empty + * {@code Optional}. This method is similar to {@link #map(Function)}, + * but the provided mapper is one whose result is already an {@code Optional}, + * and if invoked, {@code flatMap} does not wrap it with an additional + * {@code Optional}. + * + * @param The type parameter to the {@code Optional} returned by + * @param mapper a mapping function to apply to the value, if present + * the mapping function + * @return the result of applying an {@code Optional}-bearing mapping + * function to the value of this {@code Optional}, if a value is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null or returns + * a null result + */ + public Optional flatMap(Function> mapper) { + Objects.requireNonNull(mapper); + if (!isPresent()) + return empty(); + else { + return Objects.requireNonNull(mapper.apply(value)); + } + } + /** * Return the value if present, otherwise return {@code other}. * diff --git a/src/share/classes/java/util/OptionalDouble.java b/src/share/classes/java/util/OptionalDouble.java index 118a4b8d0170ebccf277a9faf5bb6f0dfab8b119..eda306cf7d422500469723125a329a0f2348a538 100644 --- a/src/share/classes/java/util/OptionalDouble.java +++ b/src/share/classes/java/util/OptionalDouble.java @@ -186,10 +186,10 @@ public final class OptionalDouble { } /** - * Indicates whether some other object is "equal to" this Optional. The + * Indicates whether some other object is "equal to" this OptionalDouble. The * other object is considered equal if: *
    - *
  • it is also an {@code OptionalInt} and; + *
  • it is also an {@code OptionalDouble} and; *
  • both instances have no value present or; *
  • the present values are "equal to" each other via {@code Double.compare() == 0}. *
@@ -226,12 +226,14 @@ public final class OptionalDouble { } /** - * Returns a non-empty string representation of this OptionalDouble suitable for + * {@inheritDoc} + * + * Returns a non-empty string representation of this object suitable for * debugging. The exact presentation format is unspecified and may vary * between implementations and versions. * * @implSpec If a value is present the result must include its string - * representation in the result. Empty and present OptionalDoubless must be + * representation in the result. Empty and present instances must be * unambiguously differentiable. * * @return the string representation of this instance diff --git a/src/share/classes/java/util/OptionalInt.java b/src/share/classes/java/util/OptionalInt.java index 755c2870b738adce40e4bf1b57ad976a2c308825..66478ce47138c04917e21618c45fe90d27d047dd 100644 --- a/src/share/classes/java/util/OptionalInt.java +++ b/src/share/classes/java/util/OptionalInt.java @@ -186,7 +186,7 @@ public final class OptionalInt { } /** - * Indicates whether some other object is "equal to" this Optional. The + * Indicates whether some other object is "equal to" this OptionalInt. The * other object is considered equal if: *
    *
  • it is also an {@code OptionalInt} and; @@ -226,12 +226,14 @@ public final class OptionalInt { } /** - * Returns a non-empty string representation of this OptionalInt suitable for + * {@inheritDoc} + * + * Returns a non-empty string representation of this object suitable for * debugging. The exact presentation format is unspecified and may vary * between implementations and versions. * * @implSpec If a value is present the result must include its string - * representation in the result. Empty and present OptionalInts must be + * representation in the result. Empty and present instances must be * unambiguously differentiable. * * @return the string representation of this instance diff --git a/src/share/classes/java/util/OptionalLong.java b/src/share/classes/java/util/OptionalLong.java index fbb1661cc250aa24e66f28305488460025e55ffd..f07cc0d3d3a82b3648e5e0882489fb64642354b4 100644 --- a/src/share/classes/java/util/OptionalLong.java +++ b/src/share/classes/java/util/OptionalLong.java @@ -186,10 +186,10 @@ public final class OptionalLong { } /** - * Indicates whether some other object is "equal to" this Optional. The + * Indicates whether some other object is "equal to" this OptionalLong. The * other object is considered equal if: *
      - *
    • it is also an {@code OptionalInt} and; + *
    • it is also an {@code OptionalLong} and; *
    • both instances have no value present or; *
    • the present values are "equal to" each other via {@code ==}. *
    @@ -226,12 +226,14 @@ public final class OptionalLong { } /** - * Returns a non-empty string representation of this OptionalLong suitable for + * {@inheritDoc} + * + * Returns a non-empty string representation of this object suitable for * debugging. The exact presentation format is unspecified and may vary * between implementations and versions. * * @implSpec If a value is present the result must include its string - * representation in the result. Empty and present OptionalLongs must be + * representation in the result. Empty and present instances must be * unambiguously differentiable. * * @return the string representation of this instance diff --git a/src/share/classes/java/util/stream/DoubleStream.java b/src/share/classes/java/util/stream/DoubleStream.java index 55d244fc4c31c9809b5ee854fa3df641c6066418..1b058abdf8bf943aa670feb9cef673e77bdcf92c 100644 --- a/src/share/classes/java/util/stream/DoubleStream.java +++ b/src/share/classes/java/util/stream/DoubleStream.java @@ -746,4 +746,26 @@ public interface DoubleStream extends BaseStream { return StreamSupport.doubleStream( new StreamSpliterators.InfiniteSupplyingSpliterator.OfDouble(Long.MAX_VALUE, s)); } + + /** + * Creates a lazy concatenated {@code DoubleStream} whose elements are all the + * elements of a first {@code DoubleStream} succeeded by all the elements of the + * second {@code DoubleStream}. The resulting stream is ordered if both + * of the input streams are ordered, and parallel if either of the input + * streams is parallel. + * + * @param a the first stream + * @param b the second stream to concatenate on to end of the first stream + * @return the concatenation of the two streams + */ + public static DoubleStream concat(DoubleStream a, DoubleStream b) { + Objects.requireNonNull(a); + Objects.requireNonNull(b); + + Spliterator.OfDouble split = new Streams.ConcatSpliterator.OfDouble( + a.spliterator(), b.spliterator()); + return (a.isParallel() || b.isParallel()) + ? StreamSupport.doubleParallelStream(split) + : StreamSupport.doubleStream(split); + } } diff --git a/src/share/classes/java/util/stream/IntStream.java b/src/share/classes/java/util/stream/IntStream.java index 3eb4409c4e590653b765f36f83bd02e138cf866a..39e0713f1cff1e864707cab177b2027be05ef4f6 100644 --- a/src/share/classes/java/util/stream/IntStream.java +++ b/src/share/classes/java/util/stream/IntStream.java @@ -800,4 +800,26 @@ public interface IntStream extends BaseStream { new Streams.RangeIntSpliterator(startInclusive, endInclusive, true)); } } + + /** + * Creates a lazy concatenated {@code IntStream} whose elements are all the + * elements of a first {@code IntStream} succeeded by all the elements of the + * second {@code IntStream}. The resulting stream is ordered if both + * of the input streams are ordered, and parallel if either of the input + * streams is parallel. + * + * @param a the first stream + * @param b the second stream to concatenate on to end of the first stream + * @return the concatenation of the two streams + */ + public static IntStream concat(IntStream a, IntStream b) { + Objects.requireNonNull(a); + Objects.requireNonNull(b); + + Spliterator.OfInt split = new Streams.ConcatSpliterator.OfInt( + a.spliterator(), b.spliterator()); + return (a.isParallel() || b.isParallel()) + ? StreamSupport.intParallelStream(split) + : StreamSupport.intStream(split); + } } diff --git a/src/share/classes/java/util/stream/LongStream.java b/src/share/classes/java/util/stream/LongStream.java index 3bc0f76a49461ee4c610c5d98141c6248845ea76..6e3bc688923ccee3ea7333b2b6e28d6cb6424b6b 100644 --- a/src/share/classes/java/util/stream/LongStream.java +++ b/src/share/classes/java/util/stream/LongStream.java @@ -765,10 +765,8 @@ public interface LongStream extends BaseStream { // Split the range in two and concatenate // Note: if the range is [Long.MIN_VALUE, Long.MAX_VALUE) then // the lower range, [Long.MIN_VALUE, 0) will be further split in two -// long m = startInclusive + Long.divideUnsigned(endExclusive - startInclusive, 2) + 1; -// return Streams.concat(range(startInclusive, m), range(m, endExclusive)); - // This is temporary until Streams.concat is supported - throw new UnsupportedOperationException(); + long m = startInclusive + Long.divideUnsigned(endExclusive - startInclusive, 2) + 1; + return concat(range(startInclusive, m), range(m, endExclusive)); } else { return StreamSupport.longStream( new Streams.RangeLongSpliterator(startInclusive, endExclusive, false)); @@ -801,13 +799,33 @@ public interface LongStream extends BaseStream { // Note: if the range is [Long.MIN_VALUE, Long.MAX_VALUE] then // the lower range, [Long.MIN_VALUE, 0), and upper range, // [0, Long.MAX_VALUE], will both be further split in two -// long m = startInclusive + Long.divideUnsigned(endInclusive - startInclusive, 2) + 1; -// return Streams.concat(range(startInclusive, m), rangeClosed(m, endInclusive)); - // This is temporary until Streams.concat is supported - throw new UnsupportedOperationException(); + long m = startInclusive + Long.divideUnsigned(endInclusive - startInclusive, 2) + 1; + return concat(range(startInclusive, m), rangeClosed(m, endInclusive)); } else { return StreamSupport.longStream( new Streams.RangeLongSpliterator(startInclusive, endInclusive, true)); } } + + /** + * Creates a lazy concatenated {@code LongStream} whose elements are all the + * elements of a first {@code LongStream} succeeded by all the elements of the + * second {@code LongStream}. The resulting stream is ordered if both + * of the input streams are ordered, and parallel if either of the input + * streams is parallel. + * + * @param a the first stream + * @param b the second stream to concatenate on to end of the first stream + * @return the concatenation of the two streams + */ + public static LongStream concat(LongStream a, LongStream b) { + Objects.requireNonNull(a); + Objects.requireNonNull(b); + + Spliterator.OfLong split = new Streams.ConcatSpliterator.OfLong( + a.spliterator(), b.spliterator()); + return (a.isParallel() || b.isParallel()) + ? StreamSupport.longParallelStream(split) + : StreamSupport.longStream(split); + } } diff --git a/src/share/classes/java/util/stream/Stream.java b/src/share/classes/java/util/stream/Stream.java index 32b3585fdbff08b161e39601169e43cb0c483a60..78264e407ec1e23c480d5be83c365c64ef4f015b 100644 --- a/src/share/classes/java/util/stream/Stream.java +++ b/src/share/classes/java/util/stream/Stream.java @@ -883,4 +883,29 @@ public interface Stream extends BaseStream> { return StreamSupport.stream( new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s)); } + + /** + * Creates a lazy concatenated {@code Stream} whose elements are all the + * elements of a first {@code Stream} succeeded by all the elements of the + * second {@code Stream}. The resulting stream is ordered if both + * of the input streams are ordered, and parallel if either of the input + * streams is parallel. + * + * @param The type of stream elements + * @param a the first stream + * @param b the second stream to concatenate on to end of the first + * stream + * @return the concatenation of the two input streams + */ + public static Stream concat(Stream a, Stream b) { + Objects.requireNonNull(a); + Objects.requireNonNull(b); + + @SuppressWarnings("unchecked") + Spliterator split = new Streams.ConcatSpliterator.OfRef<>( + (Spliterator) a.spliterator(), (Spliterator) b.spliterator()); + return (a.isParallel() || b.isParallel()) + ? StreamSupport.parallelStream(split) + : StreamSupport.stream(split); + } } diff --git a/src/share/classes/java/util/stream/Streams.java b/src/share/classes/java/util/stream/Streams.java index 11dbbe3d7caed7397914eaf32741d39458335538..1d49997fe22f94507c85363469a2393d5b72acaa 100644 --- a/src/share/classes/java/util/stream/Streams.java +++ b/src/share/classes/java/util/stream/Streams.java @@ -43,7 +43,7 @@ import java.util.function.LongConsumer; * * @since 1.8 */ -class Streams { +final class Streams { private Streams() { throw new Error("no instances"); @@ -670,4 +670,147 @@ class Streams { } } } + + abstract static class ConcatSpliterator> + implements Spliterator { + protected final T_SPLITR aSpliterator; + protected final T_SPLITR bSpliterator; + // True when no split has occurred, otherwise false + boolean beforeSplit; + // Never read after splitting + final boolean unsized; + + public ConcatSpliterator(T_SPLITR aSpliterator, T_SPLITR bSpliterator) { + this.aSpliterator = aSpliterator; + this.bSpliterator = bSpliterator; + beforeSplit = true; + // The spliterator is unsized before splitting if a and b are + // sized and the sum of the estimates overflows + unsized = aSpliterator.hasCharacteristics(SIZED) + && aSpliterator.hasCharacteristics(SIZED) + && aSpliterator.estimateSize() + bSpliterator.estimateSize() < 0; + } + + @Override + public T_SPLITR trySplit() { + T_SPLITR ret = beforeSplit ? aSpliterator : (T_SPLITR) bSpliterator.trySplit(); + beforeSplit = false; + return ret; + } + + @Override + public boolean tryAdvance(Consumer consumer) { + boolean hasNext; + if (beforeSplit) { + hasNext = aSpliterator.tryAdvance(consumer); + if (!hasNext) { + beforeSplit = false; + hasNext = bSpliterator.tryAdvance(consumer); + } + } + else + hasNext = bSpliterator.tryAdvance(consumer); + return hasNext; + } + + @Override + public void forEachRemaining(Consumer consumer) { + if (beforeSplit) + aSpliterator.forEachRemaining(consumer); + bSpliterator.forEachRemaining(consumer); + } + + @Override + public long estimateSize() { + if (beforeSplit) { + // If one or both estimates are Long.MAX_VALUE then the sum + // will either be Long.MAX_VALUE or overflow to a negative value + long size = aSpliterator.estimateSize() + bSpliterator.estimateSize(); + return (size >= 0) ? size : Long.MAX_VALUE; + } + else { + return bSpliterator.estimateSize(); + } + } + + @Override + public int characteristics() { + if (beforeSplit) { + // Concatenation loses DISTINCT and SORTED characteristics + return aSpliterator.characteristics() & bSpliterator.characteristics() + & ~(Spliterator.DISTINCT | Spliterator.SORTED + | (unsized ? Spliterator.SIZED | Spliterator.SUBSIZED : 0)); + } + else { + return bSpliterator.characteristics(); + } + } + + @Override + public Comparator getComparator() { + if (beforeSplit) + throw new IllegalStateException(); + return bSpliterator.getComparator(); + } + + static class OfRef extends ConcatSpliterator> { + OfRef(Spliterator aSpliterator, Spliterator bSpliterator) { + super(aSpliterator, bSpliterator); + } + } + + private static abstract class OfPrimitive> + extends ConcatSpliterator + implements Spliterator.OfPrimitive { + private OfPrimitive(T_SPLITR aSpliterator, T_SPLITR bSpliterator) { + super(aSpliterator, bSpliterator); + } + + @Override + public boolean tryAdvance(T_CONS action) { + boolean hasNext; + if (beforeSplit) { + hasNext = aSpliterator.tryAdvance(action); + if (!hasNext) { + beforeSplit = false; + hasNext = bSpliterator.tryAdvance(action); + } + } + else + hasNext = bSpliterator.tryAdvance(action); + return hasNext; + } + + @Override + public void forEachRemaining(T_CONS action) { + if (beforeSplit) + aSpliterator.forEachRemaining(action); + bSpliterator.forEachRemaining(action); + } + } + + static class OfInt + extends ConcatSpliterator.OfPrimitive + implements Spliterator.OfInt { + OfInt(Spliterator.OfInt aSpliterator, Spliterator.OfInt bSpliterator) { + super(aSpliterator, bSpliterator); + } + } + + static class OfLong + extends ConcatSpliterator.OfPrimitive + implements Spliterator.OfLong { + OfLong(Spliterator.OfLong aSpliterator, Spliterator.OfLong bSpliterator) { + super(aSpliterator, bSpliterator); + } + } + + static class OfDouble + extends ConcatSpliterator.OfPrimitive + implements Spliterator.OfDouble { + OfDouble(Spliterator.OfDouble aSpliterator, Spliterator.OfDouble bSpliterator) { + super(aSpliterator, bSpliterator); + } + } + } } diff --git a/test/java/util/Collection/MOAT.java b/test/java/util/Collection/MOAT.java index 7abc669d4b91d9e1b80f8206b99cd00b98464b26..a5b2c42e47b757d6704b124c53b2c8a447580804 100644 --- a/test/java/util/Collection/MOAT.java +++ b/test/java/util/Collection/MOAT.java @@ -71,6 +71,14 @@ public class MOAT { testCollection(new LinkedList()); testCollection(new LinkedList().subList(0,0)); testCollection(new TreeSet()); + testCollection(Collections.checkedList(new ArrayList(), Integer.class)); + testCollection(Collections.synchronizedList(new ArrayList())); + testCollection(Collections.checkedSet(new HashSet(), Integer.class)); + testCollection(Collections.checkedSortedSet(new TreeSet(), Integer.class)); + testCollection(Collections.checkedNavigableSet(new TreeSet(), Integer.class)); + testCollection(Collections.synchronizedSet(new HashSet())); + testCollection(Collections.synchronizedSortedSet(new TreeSet())); + testCollection(Collections.synchronizedNavigableSet(new TreeSet())); testCollection(new CopyOnWriteArrayList()); testCollection(new CopyOnWriteArrayList().subList(0,0)); @@ -98,6 +106,12 @@ public class MOAT { testMap(new Hashtable()); testMap(new ConcurrentHashMap(10, 0.5f)); testMap(new ConcurrentSkipListMap()); + testMap(Collections.checkedMap(new HashMap(), Integer.class, Integer.class)); + testMap(Collections.checkedSortedMap(new TreeMap(), Integer.class, Integer.class)); + testMap(Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class)); + testMap(Collections.synchronizedMap(new HashMap())); + testMap(Collections.synchronizedSortedMap(new TreeMap())); + testMap(Collections.synchronizedNavigableMap(new TreeMap())); // Empty collections final List emptyArray = Arrays.asList(new Integer[]{}); @@ -117,19 +131,29 @@ public class MOAT { testCollection(emptySet); testEmptySet(emptySet); testEmptySet(EMPTY_SET); + testEmptySet(Collections.emptySet()); + testEmptySet(Collections.emptySortedSet()); + testEmptySet(Collections.emptyNavigableSet()); testImmutableSet(emptySet); List emptyList = emptyList(); testCollection(emptyList); testEmptyList(emptyList); testEmptyList(EMPTY_LIST); + testEmptyList(Collections.emptyList()); testImmutableList(emptyList); Map emptyMap = emptyMap(); testMap(emptyMap); testEmptyMap(emptyMap); testEmptyMap(EMPTY_MAP); + testEmptyMap(Collections.emptyMap()); + testEmptyMap(Collections.emptySortedMap()); + testEmptyMap(Collections.emptyNavigableMap()); testImmutableMap(emptyMap); + testImmutableMap(Collections.emptyMap()); + testImmutableMap(Collections.emptySortedMap()); + testImmutableMap(Collections.emptyNavigableMap()); // Singleton collections Set singletonSet = singleton(1); diff --git a/test/java/util/Collections/CheckedIdentityMap.java b/test/java/util/Collections/CheckedIdentityMap.java index befbd23b81a238f3202a50ddc80359f1c93aa79e..4c2e9d2bf5e7d1ecc8e2eb4557cdd294b9c05017 100644 --- a/test/java/util/Collections/CheckedIdentityMap.java +++ b/test/java/util/Collections/CheckedIdentityMap.java @@ -24,59 +24,42 @@ /* * @test * @bug 6585904 + * @run testng CheckedIdentityMap * @summary Checked collections with underlying maps with identity comparisons */ import java.util.*; import static java.util.Collections.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; + +import org.testng.annotations.Test; + public class CheckedIdentityMap { - void test(String[] args) throws Throwable { + + @Test + public void testHashCode() { Map m1 = checkedMap( new IdentityHashMap(), Integer.class, Integer.class); Map m2 = checkedMap( new IdentityHashMap(), Integer.class, Integer.class); + // NB: these are unique instances. Compare vs. Integer.valueOf(1) m1.put(new Integer(1), new Integer(1)); m2.put(new Integer(1), new Integer(1)); Map.Entry e1 = m1.entrySet().iterator().next(); Map.Entry e2 = m2.entrySet().iterator().next(); - check(! e1.equals(e2)); - check(e1.hashCode() == hashCode(e1)); - check(e2.hashCode() == hashCode(e2)); + + assertNotEquals(e1, e2); + assertEquals(e1.hashCode(), hashCode(e1)); + assertEquals(e2.hashCode(), hashCode(e2)); } - int hashCode(Map.Entry e) { + static int hashCode(Map.Entry e) { return (System.identityHashCode(e.getKey()) ^ System.identityHashCode(e.getValue())); } - - //--------------------- Infrastructure --------------------------- - volatile int passed = 0, failed = 0; - void pass() {passed++;} - void fail() {failed++; Thread.dumpStack();} - void fail(String msg) {System.err.println(msg); fail();} - void unexpected(Throwable t) {failed++; t.printStackTrace();} - void check(boolean cond) {if (cond) pass(); else fail();} - void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else fail(x + " not equal to " + y);} - public static void main(String[] args) throws Throwable { - new CheckedIdentityMap().instanceMain(args);} - void instanceMain(String[] args) throws Throwable { - try {test(args);} catch (Throwable t) {unexpected(t);} - System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); - if (failed > 0) throw new AssertionError("Some tests failed");} - abstract class F {abstract void f() throws Throwable;} - void THROWS(Class k, F... fs) { - for (F f : fs) - try {f.f(); fail("Expected " + k.getName() + " not thrown");} - catch (Throwable t) { - if (k.isAssignableFrom(t.getClass())) pass(); - else unexpected(t);}} - Thread checkedThread(final Runnable r) { - return new Thread() {public void run() { - try {r.run();} catch (Throwable t) {unexpected(t);}}};} } diff --git a/test/java/util/Collections/CheckedMapBash.java b/test/java/util/Collections/CheckedMapBash.java index 61c7dd09cfe2472fead2d39c904eb49679f0b6e8..880d35b56463332f6ca609db2ea9598036676412 100644 --- a/test/java/util/Collections/CheckedMapBash.java +++ b/test/java/util/Collections/CheckedMapBash.java @@ -23,76 +23,83 @@ /* * @test - * @bug 4904067 5023830 + * @bug 4904067 5023830 7129185 * @summary Unit test for Collections.checkedMap * @author Josh Bloch + * @run testng CheckedMapBash */ import java.util.*; +import java.util.function.Supplier; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertTrue; public class CheckedMapBash { - static Random rnd = new Random(); - static Object nil = new Integer(0); - - public static void main(String[] args) { - int numItr = 100; - int mapSize = 100; - - // Linked List test - for (int i=0; i> supplier) { + Map m = supplier.get(); + Object head = nil; + + for (int j=0; j> supplier) { + Map m = supplier.get(); for (int i=0; i bashNavigableMapProvider() { + ArrayList iters = new ArrayList<>(makeCheckedMaps()); + iters.ensureCapacity(numItr * iters.size()); + for(int each=1; each < numItr; each++) { + iters.addAll( makeCheckedMaps()); + } + return iters.iterator(); + } + + @DataProvider(name = "Supplier>", parallel = true) + public static Iterator navigableMapProvider() { + return makeCheckedMaps().iterator(); } - static void fail(String s) { - throw new RuntimeException(s); + public static Collection makeCheckedMaps() { + return Arrays.asList( + new Object[]{"Collections.checkedMap(HashMap)", + (Supplier) () -> {return Collections.checkedMap(new HashMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedMap(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedMap(TreeSet).descendingSet()", + (Supplier) () -> {return Collections.checkedMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap(TreeSet)", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap().descendingSet()", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}} + ); } } diff --git a/test/java/util/Collections/CheckedSetBash.java b/test/java/util/Collections/CheckedSetBash.java index 714fcae896a822f7ab822fd96ef8cad7aebf4c52..0d87cb1595350d8a4046acc89ca85173a13eba65 100644 --- a/test/java/util/Collections/CheckedSetBash.java +++ b/test/java/util/Collections/CheckedSetBash.java @@ -23,82 +23,93 @@ /* * @test - * @bug 4904067 + * @bug 4904067 7129185 * @summary Unit test for Collections.checkedSet * @author Josh Bloch + * @run testng CheckedSetBash */ import java.util.*; +import java.util.function.Supplier; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertTrue; public class CheckedSetBash { - static Random rnd = new Random(); - - public static void main(String[] args) { - int numItr = 100; - int setSize = 100; - - for (int i=0; i> supplier) { + + Set s1 = supplier.get(); + assertTrue(s1.isEmpty()); + + AddRandoms(s1, setSize); + + Set s2 = supplier.get(); + + assertTrue(s2.isEmpty()); + + AddRandoms(s2, setSize); + + Set intersection = clone(s1, supplier); + intersection.retainAll(s2); + Set diff1 = clone(s1, supplier); diff1.removeAll(s2); + Set diff2 = clone(s2, supplier); diff2.removeAll(s1); + Set union = clone(s1, supplier); union.addAll(s2); + + if (diff1.removeAll(diff2)) + fail("Set algebra identity 2 failed"); + if (diff1.removeAll(intersection)) + fail("Set algebra identity 3 failed"); + if (diff2.removeAll(diff1)) + fail("Set algebra identity 4 failed"); + if (diff2.removeAll(intersection)) + fail("Set algebra identity 5 failed"); + if (intersection.removeAll(diff1)) + fail("Set algebra identity 6 failed"); + if (intersection.removeAll(diff1)) + fail("Set algebra identity 7 failed"); + + intersection.addAll(diff1); intersection.addAll(diff2); + if (!intersection.equals(union)) + fail("Set algebra identity 1 failed"); + + if (new HashSet(union).hashCode() != union.hashCode()) + fail("Incorrect hashCode computation."); + + Iterator e = union.iterator(); + while (e.hasNext()) + if (!intersection.remove(e.next())) + fail("Couldn't remove element from copy."); + if (!intersection.isEmpty()) + fail("Copy nonempty after deleting all elements."); + + e = union.iterator(); + while (e.hasNext()) { + Object o = e.next(); + if (!union.contains(o)) + fail("Set doesn't contain one of its elements."); + e.remove(); + if (union.contains(o)) + fail("Set contains element after deletion."); } + if (!union.isEmpty()) + fail("Set nonempty after deleting all elements."); + + s1.clear(); + if (!s1.isEmpty()) + fail("Set nonempty after clear."); } // Done inefficiently so as to exercise toArray - static Set clone(Set s) { - Set clone = newSet(); - List arrayList = Arrays.asList(s.toArray()); + static Set clone(Set s, Supplier> supplier) { + Set clone = supplier.get(); + List arrayList = Arrays.asList((T[]) s.toArray()); clone.addAll(arrayList); if (!s.equals(clone)) fail("Set not equal to copy."); @@ -109,13 +120,6 @@ public class CheckedSetBash { return clone; } - static Set newSet() { - Set s = Collections.checkedSet(new HashSet(), Integer.class); - if (!s.isEmpty()) - fail("New instance non empty."); - return s; - } - static void AddRandoms(Set s, int n) { for (int i=0; i navigableSetsProvider() { + ArrayList iters = new ArrayList<>(makeCheckedSets()); + iters.ensureCapacity(numItr * iters.size()); + for(int each=1; each < numItr; each++) { + iters.addAll( makeCheckedSets()); + } + return iters.iterator(); + } + public static Collection makeCheckedSets() { + return Arrays.asList( + new Object[]{"Collections.checkedSet(HashSet)", + (Supplier) () -> {return Collections.checkedSet(new HashSet(), Integer.class);}}, + new Object[]{"Collections.checkedSet(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedSet(new TreeSet(Collections.reverseOrder()), Integer.class);}}, + new Object[]{"Collections.checkedSet(TreeSet).descendingSet()", + (Supplier) () -> {return Collections.checkedSet(new TreeSet().descendingSet(), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet(TreeSet)", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(Collections.reverseOrder()), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet().descendingSet()", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet().descendingSet(), Integer.class);}} + ); } } diff --git a/test/java/util/Collections/EmptyCollectionSerialization.java b/test/java/util/Collections/EmptyCollectionSerialization.java index 7bbe0bf14d0373c09be292df5befdd643c8867ed..1369663ffc5a07555ae75680e0f4525fb28676e8 100644 --- a/test/java/util/Collections/EmptyCollectionSerialization.java +++ b/test/java/util/Collections/EmptyCollectionSerialization.java @@ -23,13 +23,20 @@ /* * @test - * @bug 4684279 + * @bug 4684279 7129185 * @summary Empty utility collections should be singletons * @author Josh Bloch + * @run testng EmptyCollectionSerialization */ import java.util.*; +import java.util.function.Supplier; import java.io.*; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertSame; public class EmptyCollectionSerialization { private static Object patheticDeepCopy(Object o) throws Exception { @@ -45,16 +52,48 @@ public class EmptyCollectionSerialization { return ois.readObject(); } - private static boolean isSingleton(Object o) throws Exception { - return patheticDeepCopy(o) == o; + @Test(dataProvider="SerializableSingletons") + public static void serializableSingletons(String description, Supplier o) { + try { + Object singleton = o.get(); + assertSame(o.get(), singleton, description + ": broken Supplier not returning singleton"); + Object copy = patheticDeepCopy(singleton); + assertSame( copy, singleton, description + ": " + + copy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(copy)) + + " is not the singleton " + + singleton.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(singleton))); + } catch(Exception all) { + fail(description + ": Unexpected Exception", all); + } + } + + @DataProvider(name = "SerializableSingletons", parallel = true) + public static Iterator navigableMapProvider() { + return makeSingletons().iterator(); } - public static void main(String[] args) throws Exception { - if (!isSingleton(Collections.EMPTY_SET)) - throw new Exception("EMPTY_SET"); - if (!isSingleton(Collections.EMPTY_LIST)) - throw new Exception("EMPTY_LIST"); - if (!isSingleton(Collections.EMPTY_MAP)) - throw new Exception("EMPTY_MAP"); + public static Collection makeSingletons() { + return Arrays.asList( + new Object[]{"Collections.EMPTY_LIST", + (Supplier) () -> {return Collections.EMPTY_LIST;}}, + new Object[]{"Collections.EMPTY_MAP", + (Supplier) () -> {return Collections.EMPTY_MAP;}}, + new Object[]{"Collections.EMPTY_SET", + (Supplier) () -> {return Collections.EMPTY_SET;}}, + new Object[]{"Collections.singletonMap()", + (Supplier) () -> {return Collections.emptyList();}}, + new Object[]{"Collections.emptyMap()", + (Supplier) () -> {return Collections.emptyMap();}}, + new Object[]{"Collections.emptySet()", + (Supplier) () -> {return Collections.emptySet();}}, + new Object[]{"Collections.emptySortedSet()", + (Supplier) () -> {return Collections.emptySortedSet();}}, + new Object[]{"Collections.emptySortedMap()", + (Supplier) () -> {return Collections.emptySortedMap();}}, + new Object[]{"Collections.emptyNavigableSet()", + (Supplier) () -> {return Collections.emptyNavigableSet();}}, + new Object[]{"Collections.emptyNavigableMap()", + (Supplier) () -> {return Collections.emptyNavigableMap();}} + ); } } diff --git a/test/java/util/Collections/EmptyNavigableMap.java b/test/java/util/Collections/EmptyNavigableMap.java new file mode 100644 index 0000000000000000000000000000000000000000..ba8e67b42a499ad139eab7c791dea3cc137c4c00 --- /dev/null +++ b/test/java/util/Collections/EmptyNavigableMap.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4533691 7129185 + * @summary Unit test for Collections.emptyNavigableMap + * @run testng EmptyNavigableMap + */ +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; + +public class EmptyNavigableMap { + + public static void assertInstance(T actual, Class expected) { + assertInstance(expected.isInstance(actual), null); + } + + public static void assertInstance(T actual, Class expected, String message) { + assertTrue(expected.isInstance(actual), ((null != message) ? message : "") + + " " + (actual == null ? "" : actual.getClass().getSimpleName()) + " != " + expected.getSimpleName() + ". "); + } + + public static void assertEmptyNavigableMap(Object obj) { + assertInstance(obj, NavigableMap.class); + assertTrue(((NavigableMap)obj).isEmpty() && (((NavigableMap)obj).size() == 0)); + } + + public static void assertEmptyNavigableMap(Object obj, String message) { + assertInstance(obj, NavigableMap.class, message); + assertTrue(((NavigableMap)obj).isEmpty() && (((NavigableMap)obj).size() == 0), + ((null != message) ? message : "") + " Not empty. "); + } + + public interface Thrower { + + public void run() throws T; + } + + public static void assertThrows(Thrower thrower, Class throwable) { + assertThrows(thrower, throwable, null); + } + + public static void assertThrows(Thrower thrower, Class throwable, String message) { + Throwable result; + try { + thrower.run(); + fail(((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + return; + } catch (Throwable caught) { + result = caught; + } + + assertInstance(result, throwable, ((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + } + + public static final boolean isDescending(SortedMap set) { + if (null == set.comparator()) { + // natural order + return false; + } + + if (Collections.reverseOrder() == set.comparator()) { + // reverse natural order. + return true; + } + + if (set.comparator().equals(Collections.reverseOrder(Collections.reverseOrder(set.comparator())))) { + // it's a Collections.reverseOrder(Comparator). + return true; + } + + throw new IllegalStateException("can't determine ordering for " + set); + } + + /** + * Tests that the comparator is {@code null}. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testComparatorIsNull(String description, NavigableMap navigableMap) { + Comparator comparator = navigableMap.comparator(); + + assertTrue(comparator == null || comparator == Collections.reverseOrder(), description + ": Comparator (" + comparator + ") is not null."); + } + + /** + * Tests that contains requires Comparable + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContainsRequiresComparable(String description, NavigableMap navigableMap) { + assertThrows(() -> { + navigableMap.containsKey(new Object()); + }, + ClassCastException.class, + description + ": Compareable should be required"); + } + + /** + * Tests that the contains method returns {@code false}. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContains(String description, NavigableMap navigableMap) { + assertFalse(navigableMap.containsKey(new Integer(1)), + description + ": Should not contain any elements."); + assertFalse(navigableMap.containsValue(new Integer(1)), + description + ": Should not contain any elements."); + } + + /** + * Tests that the containsAll method returns {@code false}. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContainsAll(String description, NavigableMap navigableMap) { + TreeMap treeMap = new TreeMap(); + treeMap.put("1", 1); + treeMap.put("2", 2); + treeMap.put("3", 3); + + assertFalse(navigableMap.equals(treeMap), "Should not contain any elements."); + } + + /** + * Tests that the iterator is empty. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testEmptyIterator(String description, NavigableMap navigableMap) { + assertFalse(navigableMap.keySet().iterator().hasNext(), "The iterator is not empty."); + assertFalse(navigableMap.values().iterator().hasNext(), "The iterator is not empty."); + assertFalse(navigableMap.entrySet().iterator().hasNext(), "The iterator is not empty."); + } + + /** + * Tests that the set is empty. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testIsEmpty(String description, NavigableMap navigableMap) { + assertTrue(navigableMap.isEmpty(), "The set is not empty."); + } + + /** + * Tests the headMap() method. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testHeadMap(String description, NavigableMap navigableMap) { + assertThrows( + () -> { NavigableMap ss = navigableMap.headMap(null, false); }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { NavigableMap ss = navigableMap.headMap(new Object(), true); }, + ClassCastException.class, + description + ": Must throw ClassCastException for non-Comparable element"); + + NavigableMap ss = navigableMap.headMap("1", false); + + assertEmptyNavigableMap(ss, description + ": Returned value is not empty navigable set."); + } + + /** + * Tests that the size is 0. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSizeIsZero(String description, NavigableMap navigableMap) { + assertTrue(0 == navigableMap.size(), "The size of the set is not 0."); + } + + /** + * Tests the subMap() method. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSubMap(String description, NavigableMap navigableMap) { + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(null, BigInteger.TEN); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(BigInteger.ZERO, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(null, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + Object obj1 = new Object(); + Object obj2 = new Object(); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(obj1, BigInteger.TEN); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(BigInteger.ZERO, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(obj1, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + // minimal range + navigableMap.subMap(BigInteger.ZERO, false, BigInteger.ZERO, false); + navigableMap.subMap(BigInteger.ZERO, false, BigInteger.ZERO, true); + navigableMap.subMap(BigInteger.ZERO, true, BigInteger.ZERO, false); + navigableMap.subMap(BigInteger.ZERO, true, BigInteger.ZERO, true); + + Object first = isDescending(navigableMap) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + assertThrows( + () -> { + navigableMap.subMap(last, true, first, false); + }, + IllegalArgumentException.class, description + + ": Must throw IllegalArgumentException when fromElement is not less then then toElement."); + + navigableMap.subMap(first, true, last, false); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSubMapRanges(String description, NavigableMap navigableMap) { + Object first = isDescending(navigableMap) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + NavigableMap subMap = navigableMap.subMap(first, true, last, true); + + // same subset + subMap.subMap(first, true, last, true); + + // slightly smaller + NavigableMap ns = subMap.subMap(first, false, last, false); + // slight exapansion + assertThrows(() -> { + ns.subMap(first, true, last, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.subMap(first, false, BigInteger.ONE, false); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testheadMapRanges(String description, NavigableMap navigableMap) { + NavigableMap subMap = navigableMap.headMap(BigInteger.ONE, true); + + // same subset + subMap.headMap(BigInteger.ONE, true); + + // slightly smaller + NavigableMap ns = subMap.headMap(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.headMap(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.headMap(isDescending(subMap) ? BigInteger.TEN : BigInteger.ZERO, true); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testTailMapRanges(String description, NavigableMap navigableMap) { + NavigableMap subMap = navigableMap.tailMap(BigInteger.ONE, true); + + // same subset + subMap.tailMap(BigInteger.ONE, true); + + // slightly smaller + NavigableMap ns = subMap.tailMap(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.tailMap(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.tailMap(isDescending(subMap) ? BigInteger.ZERO : BigInteger.TEN, false); + } + + /** + * Tests the tailMap() method. + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testTailMap(String description, NavigableMap navigableMap) { + assertThrows(() -> { + navigableMap.tailMap(null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows(() -> { + navigableMap.tailMap(new Object()); + }, ClassCastException.class); + + NavigableMap ss = navigableMap.tailMap("1", true); + + assertEmptyNavigableMap(ss, description + ": Returned value is not empty navigable set."); + } + + @DataProvider(name = "NavigableMap", parallel = true) + public static Iterator navigableMapsProvider() { + return makeNavigableMaps().iterator(); + } + + public static Collection makeNavigableMaps() { + return Arrays.asList( + new Object[]{"UnmodifiableNavigableMap(TreeMap)", Collections.unmodifiableNavigableMap(new TreeMap())}, + new Object[]{"UnmodifiableNavigableMap(TreeMap.descendingMap()", Collections.unmodifiableNavigableMap(new TreeMap().descendingMap())}, + new Object[]{"UnmodifiableNavigableMap(TreeMap.descendingMap().descendingMap()", Collections.unmodifiableNavigableMap(new TreeMap().descendingMap().descendingMap())}, + new Object[]{"emptyNavigableMap()", Collections.emptyNavigableMap()}, + new Object[]{"emptyNavigableMap().descendingMap()", Collections.emptyNavigableMap().descendingMap()}, + new Object[]{"emptyNavigableMap().descendingMap().descendingMap()", Collections.emptyNavigableMap().descendingMap().descendingMap()} + ); + } +} diff --git a/test/java/util/Collections/EmptyNavigableSet.java b/test/java/util/Collections/EmptyNavigableSet.java new file mode 100644 index 0000000000000000000000000000000000000000..7c4811bdfe849c016be55f0ce0242c348f87e0e8 --- /dev/null +++ b/test/java/util/Collections/EmptyNavigableSet.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4533691 7129185 + * @summary Unit test for Collections.emptyNavigableSet + * @run testng EmptyNavigableSet + */ +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.TreeSet; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; + +public class EmptyNavigableSet { + + public static void assertInstance(T actual, Class expected) { + assertInstance(expected.isInstance(actual), null); + } + + public static void assertInstance(T actual, Class expected, String message) { + assertTrue(expected.isInstance(actual), ((null != message) ? message : "") + + " " + (actual == null ? "" : actual.getClass().getSimpleName()) + " != " + expected.getSimpleName() + ". "); + } + + public static void assertEmptyNavigableSet(Object obj) { + assertInstance(obj, NavigableSet.class); + assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0)); + } + + public static void assertEmptyNavigableSet(Object obj, String message) { + assertInstance(obj, NavigableSet.class, message); + assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0), + ((null != message) ? message : "") + " Not empty. "); + } + + public interface Thrower { + + public void run() throws T; + } + + public static void assertThrows(Thrower thrower, Class throwable) { + assertThrows(thrower, throwable, null); + } + + public static void assertThrows(Thrower thrower, Class throwable, String message) { + Throwable result; + try { + thrower.run(); + fail(((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + return; + } catch (Throwable caught) { + result = caught; + } + + assertInstance(result, throwable, ((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + } + + public static final boolean isDescending(SortedSet set) { + if (null == set.comparator()) { + // natural order + return false; + } + + if (Collections.reverseOrder() == set.comparator()) { + // reverse natural order. + return true; + } + + if (set.comparator().equals(Collections.reverseOrder(Collections.reverseOrder(set.comparator())))) { + // it's a Collections.reverseOrder(Comparator). + return true; + } + + throw new IllegalStateException("can't determine ordering for " + set); + } + + /** + * Tests that the comparator is {@code null}. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testComparatorIsNull(String description, NavigableSet navigableSet) { + Comparator comparator = navigableSet.comparator(); + + assertTrue(comparator == null || comparator == Collections.reverseOrder(), description + ": Comparator (" + comparator + ") is not null."); + } + + /** + * Tests that contains requires Comparable + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContainsRequiresComparable(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.contains(new Object()); + }, + ClassCastException.class, + description + ": Compareable should be required"); + } + + /** + * Tests that the contains method returns {@code false}. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContains(String description, NavigableSet navigableSet) { + assertFalse(navigableSet.contains(new Integer(1)), + description + ": Should not contain any elements."); + } + + /** + * Tests that the containsAll method returns {@code false}. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContainsAll(String description, NavigableSet navigableSet) { + TreeSet treeSet = new TreeSet(); + treeSet.add("1"); + treeSet.add("2"); + treeSet.add("3"); + + assertFalse(navigableSet.containsAll(treeSet), "Should not contain any elements."); + } + + /** + * Tests that the iterator is empty. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testEmptyIterator(String description, NavigableSet navigableSet) { + Iterator emptyIterator = navigableSet.iterator(); + + assertFalse((emptyIterator != null) && (emptyIterator.hasNext()), + "The iterator is not empty."); + } + + /** + * Tests that the set is empty. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testIsEmpty(String description, NavigableSet navigableSet) { + assertTrue(navigableSet.isEmpty(), "The set is not empty."); + } + + /** + * Tests that the first() method throws NoSuchElementException + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testFirst(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.first(); + }, NoSuchElementException.class, description); + } + + /** + * Tests the headSet() method. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testHeadSet(String description, NavigableSet navigableSet) { + assertThrows( + () -> { NavigableSet ns = navigableSet.headSet(null, false); }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { NavigableSet ns = navigableSet.headSet(new Object(), true); }, + ClassCastException.class, + description + ": Must throw ClassCastException for non-Comparable element"); + + NavigableSet ns = navigableSet.headSet("1", false); + + assertEmptyNavigableSet(ns, description + ": Returned value is not empty navigable set."); + } + + /** + * Tests that the last() method throws NoSuchElementException + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testLast(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.last(); + }, NoSuchElementException.class, description); + } + + /** + * Tests that the size is 0. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSizeIsZero(String description, NavigableSet navigableSet) { + assertTrue(0 == navigableSet.size(), "The size of the set is not 0."); + } + + /** + * Tests the subSet() method. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSubSet(String description, NavigableSet navigableSet) { + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(null, BigInteger.TEN); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(BigInteger.ZERO, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(null, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + Object obj1 = new Object(); + Object obj2 = new Object(); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(obj1, BigInteger.TEN); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(BigInteger.ZERO, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(obj1, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + // minimal range + navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, false); + navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, true); + navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, false); + navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, true); + + Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + assertThrows( + () -> { + navigableSet.subSet(last, true, first, false); + }, + IllegalArgumentException.class, description + + ": Must throw IllegalArgumentException when fromElement is not less then then toElement."); + + navigableSet.subSet(first, true, last, false); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSubSetRanges(String description, NavigableSet navigableSet) { + Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + NavigableSet subSet = navigableSet.subSet(first, true, last, true); + + // same subset + subSet.subSet(first, true, last, true); + + // slightly smaller + NavigableSet ns = subSet.subSet(first, false, last, false); + // slight exapansion + assertThrows(() -> { + ns.subSet(first, true, last, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.subSet(first, false, BigInteger.ONE, false); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testheadSetRanges(String description, NavigableSet navigableSet) { + NavigableSet subSet = navigableSet.headSet(BigInteger.ONE, true); + + // same subset + subSet.headSet(BigInteger.ONE, true); + + // slightly smaller + NavigableSet ns = subSet.headSet(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.headSet(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.headSet(isDescending(subSet) ? BigInteger.TEN : BigInteger.ZERO, true); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testTailSetRanges(String description, NavigableSet navigableSet) { + NavigableSet subSet = navigableSet.tailSet(BigInteger.ONE, true); + + // same subset + subSet.tailSet(BigInteger.ONE, true); + + // slightly smaller + NavigableSet ns = subSet.tailSet(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.tailSet(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.tailSet(isDescending(subSet) ? BigInteger.ZERO : BigInteger.TEN, false); + } + + /** + * Tests the tailSet() method. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testTailSet(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.tailSet(null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); + + assertThrows(() -> { + navigableSet.tailSet(new Object()); + }, ClassCastException.class); + + NavigableSet ss = navigableSet.tailSet("1", true); + + assertEmptyNavigableSet(ss, description + ": Returned value is not empty navigable set."); + } + + /** + * Tests that the array has a size of 0. + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testToArray(String description, NavigableSet navigableSet) { + Object[] emptyNavigableSetArray = navigableSet.toArray(); + + assertTrue(emptyNavigableSetArray.length == 0, "Returned non-empty Array."); + + emptyNavigableSetArray = new Object[20]; + + Object[] result = navigableSet.toArray(emptyNavigableSetArray); + + assertSame(emptyNavigableSetArray, result); + + assertTrue(result[0] == null); + } + + @DataProvider(name = "NavigableSet", parallel = true) + public static Iterator navigableSetsProvider() { + return makeNavigableSets().iterator(); + } + + public static Collection makeNavigableSets() { + return Arrays.asList( + new Object[]{"UnmodifiableNavigableSet(TreeSet)", Collections.unmodifiableNavigableSet(new TreeSet())}, + new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet())}, + new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet().descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet().descendingSet())}, + new Object[]{"emptyNavigableSet()", Collections.emptyNavigableSet()}, + new Object[]{"emptyNavigableSet().descendingSet()", Collections.emptyNavigableSet().descendingSet()}, + new Object[]{"emptyNavigableSet().descendingSet().descendingSet()", Collections.emptyNavigableSet().descendingSet().descendingSet()} + ); + } +} diff --git a/test/java/util/Collections/EmptySortedSet.java b/test/java/util/Collections/EmptySortedSet.java deleted file mode 100644 index 224400ec7a8d8e5e86f76c6a58143b96bd38a77d..0000000000000000000000000000000000000000 --- a/test/java/util/Collections/EmptySortedSet.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 4533691 - * @summary Unit test for Collections.emptySortedSet - */ - -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.SortedSet; -import java.util.TreeSet; - -public class EmptySortedSet { - static int status = 0; - private static final String FAILED = " failed. "; - private static final String PERIOD = "."; - private final String thisClassName = this.getClass().getName(); - - public static void main(String[] args) throws Exception { - new EmptySortedSet(); - } - - public EmptySortedSet() throws Exception { - run(); - } - - /** - * Returns {@code true} if the {@link Object} passed in is an empty - * {@link SortedSet}. - * - * @param obj the object to test - * @return {@code true} if the {@link Object} is an empty {@link SortedSet} - * otherwise {@code false}. - */ - private boolean isEmptySortedSet(Object obj) { - boolean isEmptySortedSet = false; - - // We determine if the object is an empty sorted set by testing if it's - // an instance of SortedSet, and if so, if it's empty. Currently the - // testing doesn't include checks of the other methods. - if (obj instanceof SortedSet) { - SortedSet ss = (SortedSet) obj; - - if ((ss.isEmpty()) && (ss.size() == 0)) { - isEmptySortedSet = true; - } - } - - return isEmptySortedSet; - } - - private void run() throws Exception { - Method[] methods = this.getClass().getDeclaredMethods(); - - for (int i = 0; i < methods.length; i++) { - Method method = methods[i]; - String methodName = method.getName(); - - if (methodName.startsWith("test")) { - try { - Object obj = method.invoke(this, new Object[0]); - } catch(Exception e) { - throw new Exception(this.getClass().getName() + "." + - methodName + " test failed, test exception " - + "follows\n" + e.getCause()); - } - } - } - } - - private void throwException(String methodName, String reason) - throws Exception - { - StringBuilder sb = new StringBuilder(thisClassName); - sb.append(PERIOD); - sb.append(methodName); - sb.append(FAILED); - sb.append(reason); - throw new Exception(sb.toString()); - } - - /** - * - */ - private void test00() throws Exception { - //throwException("test00", "This test has not been implemented yet."); - } - - /** - * Tests that the comparator is {@code null}. - */ - private void testComparatorIsNull() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Comparator comparator = sortedSet.comparator(); - - if (comparator != null) { - throwException("testComparatorIsNull", "Comparator is not null."); - } - } - - /** - * Tests that the contains method returns {@code false}. - */ - private void testContains() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if (sortedSet.contains(new Object())) { - throwException("testContains", "Should not contain any elements."); - } - } - - /** - * Tests that the containsAll method returns {@code false}. - */ - private void testContainsAll() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - TreeSet treeSet = new TreeSet(); - treeSet.add("1"); - treeSet.add("2"); - treeSet.add("3"); - - if (sortedSet.containsAll(treeSet)) { - throwException("testContainsAll", - "Should not contain any elements."); - } - } - - /** - * Tests that the iterator is empty. - */ - private void testEmptyIterator() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Iterator emptyIterator = sortedSet.iterator(); - - if ((emptyIterator != null) && (emptyIterator.hasNext())) { - throwException("testEmptyIterator", "The iterator is not empty."); - } - } - - /** - * Tests that the set is empty. - */ - private void testIsEmpty() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if ((sortedSet != null) && (!sortedSet.isEmpty())) { - throwException("testSizeIsZero", "The set is not empty."); - } - } - - /** - * Tests that the first() method throws NoSuchElementException - */ - private void testFirst() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - try { - sortedSet.first(); - throwException("testFirst", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } - } - - /** - * Tests the headSet() method. - */ - private void testHeadSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; - - try { - ss = sortedSet.headSet(null); - throwException("testHeadSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } - - try { - ss = sortedSet.headSet(new Object()); - throwException("testHeadSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } - - ss = sortedSet.headSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testHeadSet", - "Returned value is null or not an EmptySortedSet."); - } - } - - /** - * Tests that the last() method throws NoSuchElementException - */ - private void testLast() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - try { - sortedSet.last(); - throwException("testLast", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } - } - - /** - * Tests that the size is 0. - */ - private void testSizeIsZero() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - int size = sortedSet.size(); - - if (size > 0) { - throwException("testSizeIsZero", - "The size of the set is greater then 0."); - } - } - - /** - * Tests the subSet() method. - */ - private void testSubSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss = sortedSet.headSet("1"); - - try { - ss = sortedSet.subSet(null, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, null); - ss = sortedSet.subSet(null, null); - throwException("testSubSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } - - try { - Object obj1 = new Object(); - Object obj2 = new Object(); - ss = sortedSet.subSet(obj1, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, obj2); - ss = sortedSet.subSet(obj1, obj2); - throwException("testSubSet", - "Must throw ClassCastException for parameter which is " - + "not Comparable."); - } catch(ClassCastException cce) { - // Do nothing. - } - - try { - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.ZERO); - ss = sortedSet.subSet(BigInteger.TEN, BigInteger.ZERO); - throwException("testSubSet", - "Must throw IllegalArgumentException when fromElement is " - + "not less then then toElement."); - } catch(IllegalArgumentException iae) { - // Do nothing. - } - - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.TEN); - - if (!isEmptySortedSet(ss)) { - throw new Exception("Returned value is not empty sorted set."); - } - } - - /** - * Tests the tailSet() method. - */ - private void testTailSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; - - try { - ss = sortedSet.tailSet(null); - throwException("testTailSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } - - try { - SortedSet ss2 = sortedSet.tailSet(new Object()); - throwException("testTailSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } - - ss = sortedSet.tailSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testTailSet", - "Returned value is null or not an EmptySortedSet."); - } - } - - /** - * Tests that the array has a size of 0. - */ - private void testToArray() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Object[] emptySortedSetArray = sortedSet.toArray(); - - if ((emptySortedSetArray == null) || (emptySortedSetArray.length > 0)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } - - String[] strings = new String[2]; - strings[0] = "1"; - strings[1] = "2"; - emptySortedSetArray = sortedSet.toArray(strings); - - if ((emptySortedSetArray == null) || (emptySortedSetArray[0] != null)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } - } -} diff --git a/test/java/util/Map/LockStep.java b/test/java/util/Map/LockStep.java index 553644782ce193bdbbf8ee6c24feeea8cd267e9b..2903c5a5f506d7d12a8326f76de50b700a63d787 100644 --- a/test/java/util/Map/LockStep.java +++ b/test/java/util/Map/LockStep.java @@ -100,7 +100,14 @@ public class LockStep { new Hashtable(16), new TreeMap(), new ConcurrentHashMap(16), - new ConcurrentSkipListMap() }); + new ConcurrentSkipListMap(), + Collections.checkedMap(new HashMap(16), Integer.class, Integer.class), + Collections.checkedSortedMap(new TreeMap(), Integer.class, Integer.class), + Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class), + Collections.synchronizedMap(new HashMap(16)), + Collections.synchronizedSortedMap(new TreeMap()), + Collections.synchronizedNavigableMap(new TreeMap()) + }); for (int j = 0; j < 10; j++) put(maps, r.nextInt(100), r.nextInt(100)); diff --git a/test/java/util/NavigableMap/LockStep.java b/test/java/util/NavigableMap/LockStep.java index 64f9bdf7683bdd19d46e13aa3ad9ef9f2f7bad90..0b43d0a0744e7cc0a696efa5203cfb1bf1d41527 100644 --- a/test/java/util/NavigableMap/LockStep.java +++ b/test/java/util/NavigableMap/LockStep.java @@ -55,11 +55,19 @@ public class LockStep { lockSteps(new TreeMap(), new ConcurrentSkipListMap()); + lockSteps(new TreeMap(), + Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class)); + lockSteps(new TreeMap(), + Collections.synchronizedNavigableMap(new TreeMap())); lockSteps(new TreeMap(reverseOrder()), new ConcurrentSkipListMap(reverseOrder())); lockSteps(new TreeSet(), new ConcurrentSkipListSet()); + lockSteps(new TreeSet(), + Collections.checkedNavigableSet(new TreeSet(), Integer.class)); + lockSteps(new TreeSet(), + Collections.synchronizedNavigableSet(new TreeSet())); lockSteps(new TreeSet(reverseOrder()), new ConcurrentSkipListSet(reverseOrder())); } @@ -181,7 +189,15 @@ public class LockStep { testEmptyCollection(m.values()); } - static final Random rnd = new Random(); + static final Random rnd; + + static { + // sufficiently random for this test + long seed = System.nanoTime(); + System.out.println(LockStep.class.getCanonicalName() + ": Trial random seed: " + seed ); + + rnd = new Random(seed); + } static void equalNext(final Iterator it, Object expected) { if (maybe(2)) @@ -208,8 +224,15 @@ public class LockStep { check(s.descendingSet().descendingSet().comparator() == null); equal(s.isEmpty(), s.size() == 0); equal2(s, s.descendingSet()); - if (maybe(4) && s instanceof Serializable) - equal2(s, serialClone(s)); + if (maybe(4) && s instanceof Serializable) { + try { + equal2(s, serialClone(s)); + } catch(RuntimeException uhoh) { + if(!(uhoh.getCause() instanceof NotSerializableException)) { + throw uhoh; + } + } + } Comparator cmp = comparator(s); if (s.isEmpty()) { THROWS(NoSuchElementException.class, @@ -276,6 +299,15 @@ public class LockStep { check(! it2.hasNext()); } + static void equalSetsLeaf(final Set s1, final Set s2) { + equal2(s1, s2); + equal( s1.size(), s2.size()); + equal( s1.isEmpty(), s2.isEmpty()); + equal( s1.hashCode(), s2.hashCode()); + equal( s1.toString(), s2.toString()); + equal( s1.containsAll(s2), s2.containsAll(s1)); + } + static void equalNavigableSetsLeaf(final NavigableSet s1, final NavigableSet s2) { equal2(s1, s2); @@ -448,8 +480,7 @@ public class LockStep { static void equalNavigableMaps(NavigableMap m1, NavigableMap m2) { equalNavigableMapsLeaf(m1, m2); - equalNavigableSetsLeaf((NavigableSet) m1.keySet(), - (NavigableSet) m2.keySet()); + equalSetsLeaf(m1.keySet(), m2.keySet()); equalNavigableSets(m1.navigableKeySet(), m2.navigableKeySet()); equalNavigableSets(m1.descendingKeySet(), @@ -836,5 +867,7 @@ public class LockStep { @SuppressWarnings("unchecked") static T serialClone(T obj) { try { return (T) readObject(serializedForm(obj)); } - catch (Exception e) { throw new RuntimeException(e); }} + catch (Error|RuntimeException e) { throw e; } + catch (Throwable e) { throw new RuntimeException(e); } + } } diff --git a/test/java/util/Optional/Basic.java b/test/java/util/Optional/Basic.java index 099e045598571eb7ec9803acf2995801a26f01d9..dc05ff97a7835a906df8854a6b3155e085dea1ed 100644 --- a/test/java/util/Optional/Basic.java +++ b/test/java/util/Optional/Basic.java @@ -58,36 +58,36 @@ public class Basic { assertSame(Boolean.FALSE, empty.orElseGet(()-> Boolean.FALSE)); } - @Test(expectedExceptions=NoSuchElementException.class) - public void testEmptyGet() { - Optional empty = Optional.empty(); + @Test(expectedExceptions=NoSuchElementException.class) + public void testEmptyGet() { + Optional empty = Optional.empty(); - Boolean got = empty.get(); - } + Boolean got = empty.get(); + } - @Test(expectedExceptions=NullPointerException.class) - public void testEmptyOrElseGetNull() { - Optional empty = Optional.empty(); + @Test(expectedExceptions=NullPointerException.class) + public void testEmptyOrElseGetNull() { + Optional empty = Optional.empty(); - Boolean got = empty.orElseGet(null); - } + Boolean got = empty.orElseGet(null); + } - @Test(expectedExceptions=NullPointerException.class) - public void testEmptyOrElseThrowNull() throws Throwable { - Optional empty = Optional.empty(); + @Test(expectedExceptions=NullPointerException.class) + public void testEmptyOrElseThrowNull() throws Throwable { + Optional empty = Optional.empty(); - Boolean got = empty.orElseThrow(null); - } + Boolean got = empty.orElseThrow(null); + } - @Test(expectedExceptions=ObscureException.class) - public void testEmptyOrElseThrow() throws Exception { - Optional empty = Optional.empty(); + @Test(expectedExceptions=ObscureException.class) + public void testEmptyOrElseThrow() throws Exception { + Optional empty = Optional.empty(); - Boolean got = empty.orElseThrow(ObscureException::new); - } + Boolean got = empty.orElseThrow(ObscureException::new); + } - @Test(groups = "unit") - public void testPresent() { + @Test(groups = "unit") + public void testPresent() { Optional empty = Optional.empty(); Optional presentEmptyString = Optional.of(""); Optional present = Optional.of(Boolean.TRUE); @@ -116,6 +116,116 @@ public class Basic { assertSame(Boolean.TRUE, present.orElseThrow(ObscureException::new)); } + @Test(groups = "unit") + public void testOfNullable() { + Optional instance = Optional.ofNullable(null); + assertFalse(instance.isPresent()); + + instance = Optional.ofNullable("Duke"); + assertTrue(instance.isPresent()); + assertEquals(instance.get(), "Duke"); + } + + @Test(groups = "unit") + public void testFilter() { + // Null mapper function + Optional empty = Optional.empty(); + Optional duke = Optional.of("Duke"); + + try { + Optional result = empty.filter(null); + fail("Should throw NPE on null mapping function"); + } catch (NullPointerException npe) { + // expected + } + + Optional result = empty.filter(String::isEmpty); + assertFalse(result.isPresent()); + + result = duke.filter(String::isEmpty); + assertFalse(result.isPresent()); + result = duke.filter(s -> s.startsWith("D")); + assertTrue(result.isPresent()); + assertEquals(result.get(), "Duke"); + + Optional emptyString = Optional.of(""); + result = emptyString.filter(String::isEmpty); + assertTrue(result.isPresent()); + assertEquals(result.get(), ""); + } + + @Test(groups = "unit") + public void testMap() { + Optional empty = Optional.empty(); + Optional duke = Optional.of("Duke"); + + // Null mapper function + try { + Optional b = empty.map(null); + fail("Should throw NPE on null mapping function"); + } catch (NullPointerException npe) { + // expected + } + + // Map an empty value + Optional b = empty.map(String::isEmpty); + assertFalse(b.isPresent()); + + // Map into null + b = empty.map(n -> null); + assertFalse(b.isPresent()); + b = duke.map(s -> null); + assertFalse(b.isPresent()); + + // Map to value + Optional l = duke.map(String::length); + assertEquals(l.get().intValue(), 4); + } + + @Test(groups = "unit") + public void testFlatMap() { + Optional empty = Optional.empty(); + Optional duke = Optional.of("Duke"); + + // Null mapper function + try { + Optional b = empty.flatMap(null); + fail("Should throw NPE on null mapping function"); + } catch (NullPointerException npe) { + // expected + } + + // Map into null + try { + Optional b = duke.flatMap(s -> null); + fail("Should throw NPE when mapper return null"); + } catch (NullPointerException npe) { + // expected + } + + // Empty won't invoke mapper function + try { + Optional b = empty.flatMap(s -> null); + assertFalse(b.isPresent()); + } catch (NullPointerException npe) { + fail("Mapper function should not be invoked"); + } + + // Map an empty value + Optional l = empty.flatMap(s -> Optional.of(s.length())); + assertFalse(l.isPresent()); + + // Map to value + Optional fixture = Optional.of(Integer.MAX_VALUE); + l = duke.flatMap(s -> Optional.of(s.length())); + assertTrue(l.isPresent()); + assertEquals(l.get().intValue(), 4); + + // Verify same instance + l = duke.flatMap(s -> fixture); + assertSame(l, fixture); + } + private static class ObscureException extends RuntimeException { } diff --git a/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java b/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java index 225cbf147f659c257b9bfc4e201f9755182de856..cec872aa4841b2ade715b06d29864006d7051a71 100644 --- a/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java +++ b/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java @@ -360,35 +360,26 @@ public class LambdaTestHelpers { private static Map toBoxedMultiset(Iterator it) { Map result = new HashMap<>(); - it.forEachRemaining(new OmnivorousConsumer() { - @Override - public void accept(T t) { - add(t); - } - - @Override - public void accept(int value) { - add(value); - } + it.forEachRemaining(toBoxingConsumer(o -> { + if (result.containsKey(o)) + result.put(o, result.get(o) + 1); + else + result.put(o, 1); + })); - @Override - public void accept(long value) { - add(value); - } + return (Map) result; + } - @Override - public void accept(double value) { - add(value); - } + @SuppressWarnings("unchecked") + public static Map toBoxedMultiset(Spliterator it) { + Map result = new HashMap<>(); - void add(Object o) { + it.forEachRemaining(toBoxingConsumer(o -> { if (result.containsKey(o)) result.put(o, result.get(o) + 1); else result.put(o, 1); - } - - }); + })); return (Map) result; } diff --git a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatOpTest.java b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatOpTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1b3c4b65bf243ef7b431d982836ee0e2556f521f --- /dev/null +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatOpTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.tests.java.util.stream; + +import java.util.stream.OpTestCase; +import java.util.stream.StreamTestDataProvider; + +import org.testng.annotations.Test; + +import java.util.stream.Stream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.DoubleStream; +import java.util.stream.TestData; + +import static java.util.stream.LambdaTestHelpers.*; + +public class ConcatOpTest extends OpTestCase { + + // Sanity to make sure all type of stream source works + @Test(dataProvider = "StreamTestData", dataProviderClass = StreamTestDataProvider.class) + public void testOpsSequential(String name, TestData.OfRef data) { + exerciseOpsInt(data, + s -> Stream.concat(s, data.stream()), + s -> IntStream.concat(s, data.stream().mapToInt(Integer::intValue)), + s -> LongStream.concat(s, data.stream().mapToLong(Integer::longValue)), + s -> DoubleStream.concat(s, data.stream().mapToDouble(Integer::doubleValue))); + } +} diff --git a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatTest.java b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ce9c9645b648c1be91788f1e2abf27420d7ef5f --- /dev/null +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ConcatTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.tests.java.util.stream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Spliterator; +import java.util.TreeSet; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import static java.util.stream.LambdaTestHelpers.*; +import static org.testng.Assert.*; + +@Test +public class ConcatTest { + private static Object[][] cases; + + static { + List part1 = Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4); + List part2 = Arrays.asList(8, 8, 6, 6, 9, 7, 10, 9); + List p1p2 = Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 8, 8, 6, 6, 9, 7, 10, 9); + List p2p1 = Arrays.asList(8, 8, 6, 6, 9, 7, 10, 9, 5, 3, 4, 1, 2, 6, 2, 4); + List empty = new LinkedList<>(); // To be ordered + assertTrue(empty.isEmpty()); + LinkedHashSet distinctP1 = new LinkedHashSet<>(part1); + LinkedHashSet distinctP2 = new LinkedHashSet<>(part2); + TreeSet sortedP1 = new TreeSet<>(part1); + TreeSet sortedP2 = new TreeSet<>(part2); + + cases = new Object[][] { + { "regular", part1, part2, p1p2 }, + { "reverse regular", part2, part1, p2p1 }, + { "front distinct", distinctP1, part2, Arrays.asList(5, 3, 4, 1, 2, 6, 8, 8, 6, 6, 9, 7, 10, 9) }, + { "back distinct", part1, distinctP2, Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 8, 6, 9, 7, 10) }, + { "both distinct", distinctP1, distinctP2, Arrays.asList(5, 3, 4, 1, 2, 6, 8, 6, 9, 7, 10) }, + { "front sorted", sortedP1, part2, Arrays.asList(1, 2, 3, 4, 5, 6, 8, 8, 6, 6, 9, 7, 10, 9) }, + { "back sorted", part1, sortedP2, Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 6, 7, 8, 9, 10) }, + { "both sorted", sortedP1, sortedP2, Arrays.asList(1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10) }, + { "reverse both sorted", sortedP2, sortedP1, Arrays.asList(6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6) }, + { "empty something", empty, part1, part1 }, + { "something empty", part1, empty, part1 }, + { "empty empty", empty, empty, empty } + }; + } + + @DataProvider(name = "cases") + private static Object[][] getCases() { + return cases; + } + + @Factory(dataProvider = "cases") + public static Object[] createTests(String scenario, Collection c1, Collection c2, Collection expected) { + return new Object[] { + new ConcatTest(scenario, c1, c2, expected) + }; + } + + protected final String scenario; + protected final Collection c1; + protected final Collection c2; + protected final Collection expected; + + public ConcatTest(String scenario, Collection c1, Collection c2, Collection expected) { + this.scenario = scenario; + this.c1 = c1; + this.c2 = c2; + this.expected = expected; + + // verify prerequisite + Stream s1s = c1.stream(); + Stream s2s = c2.stream(); + Stream s1p = c1.parallelStream(); + Stream s2p = c2.parallelStream(); + assertTrue(s1p.isParallel()); + assertTrue(s2p.isParallel()); + assertFalse(s1s.isParallel()); + assertFalse(s2s.isParallel()); + + assertTrue(s1s.spliterator().hasCharacteristics(Spliterator.ORDERED)); + assertTrue(s1p.spliterator().hasCharacteristics(Spliterator.ORDERED)); + assertTrue(s2s.spliterator().hasCharacteristics(Spliterator.ORDERED)); + assertTrue(s2p.spliterator().hasCharacteristics(Spliterator.ORDERED)); + } + + private void assertConcatContent(Spliterator sp, boolean ordered, Spliterator expected) { + // concat stream cannot guarantee uniqueness + assertFalse(sp.hasCharacteristics(Spliterator.DISTINCT), scenario); + // concat stream cannot guarantee sorted + assertFalse(sp.hasCharacteristics(Spliterator.SORTED), scenario); + // concat stream is ordered if both are ordered + assertEquals(sp.hasCharacteristics(Spliterator.ORDERED), ordered, scenario); + + // Verify elements + if (ordered) { + assertEquals(toBoxedList(sp), + toBoxedList(expected), + scenario); + } else { + assertEquals(toBoxedMultiset(sp), + toBoxedMultiset(expected), + scenario); + } + } + + private void assertRefConcat(Stream s1, Stream s2, boolean parallel, boolean ordered) { + Stream result = Stream.concat(s1, s2); + assertEquals(result.isParallel(), parallel); + assertConcatContent(result.spliterator(), ordered, expected.spliterator()); + } + + private void assertIntConcat(Stream s1, Stream s2, boolean parallel, boolean ordered) { + IntStream result = IntStream.concat(s1.mapToInt(Integer::intValue), + s2.mapToInt(Integer::intValue)); + assertEquals(result.isParallel(), parallel); + assertConcatContent(result.spliterator(), ordered, + expected.stream().mapToInt(Integer::intValue).spliterator()); + } + + private void assertLongConcat(Stream s1, Stream s2, boolean parallel, boolean ordered) { + LongStream result = LongStream.concat(s1.mapToLong(Integer::longValue), + s2.mapToLong(Integer::longValue)); + assertEquals(result.isParallel(), parallel); + assertConcatContent(result.spliterator(), ordered, + expected.stream().mapToLong(Integer::longValue).spliterator()); + } + + private void assertDoubleConcat(Stream s1, Stream s2, boolean parallel, boolean ordered) { + DoubleStream result = DoubleStream.concat(s1.mapToDouble(Integer::doubleValue), + s2.mapToDouble(Integer::doubleValue)); + assertEquals(result.isParallel(), parallel); + assertConcatContent(result.spliterator(), ordered, + expected.stream().mapToDouble(Integer::doubleValue).spliterator()); + } + + public void testRefConcat() { + // sequential + sequential -> sequential + assertRefConcat(c1.stream(), c2.stream(), false, true); + // parallel + parallel -> parallel + assertRefConcat(c1.parallelStream(), c2.parallelStream(), true, true); + // sequential + parallel -> parallel + assertRefConcat(c1.stream(), c2.parallelStream(), true, true); + // parallel + sequential -> parallel + assertRefConcat(c1.parallelStream(), c2.stream(), true, true); + + // not ordered + assertRefConcat(c1.stream().unordered(), c2.stream(), false, false); + assertRefConcat(c1.stream(), c2.stream().unordered(), false, false); + assertRefConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false); + } + + public void testIntConcat() { + // sequential + sequential -> sequential + assertIntConcat(c1.stream(), c2.stream(), false, true); + // parallel + parallel -> parallel + assertIntConcat(c1.parallelStream(), c2.parallelStream(), true, true); + // sequential + parallel -> parallel + assertIntConcat(c1.stream(), c2.parallelStream(), true, true); + // parallel + sequential -> parallel + assertIntConcat(c1.parallelStream(), c2.stream(), true, true); + + // not ordered + assertIntConcat(c1.stream().unordered(), c2.stream(), false, false); + assertIntConcat(c1.stream(), c2.stream().unordered(), false, false); + assertIntConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false); + } + + public void testLongConcat() { + // sequential + sequential -> sequential + assertLongConcat(c1.stream(), c2.stream(), false, true); + // parallel + parallel -> parallel + assertLongConcat(c1.parallelStream(), c2.parallelStream(), true, true); + // sequential + parallel -> parallel + assertLongConcat(c1.stream(), c2.parallelStream(), true, true); + // parallel + sequential -> parallel + assertLongConcat(c1.parallelStream(), c2.stream(), true, true); + + // not ordered + assertLongConcat(c1.stream().unordered(), c2.stream(), false, false); + assertLongConcat(c1.stream(), c2.stream().unordered(), false, false); + assertLongConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false); + } + + public void testDoubleConcat() { + // sequential + sequential -> sequential + assertDoubleConcat(c1.stream(), c2.stream(), false, true); + // parallel + parallel -> parallel + assertDoubleConcat(c1.parallelStream(), c2.parallelStream(), true, true); + // sequential + parallel -> parallel + assertDoubleConcat(c1.stream(), c2.parallelStream(), true, true); + // parallel + sequential -> parallel + assertDoubleConcat(c1.parallelStream(), c2.stream(), true, true); + + // not ordered + assertDoubleConcat(c1.stream().unordered(), c2.stream(), false, false); + assertDoubleConcat(c1.stream(), c2.stream().unordered(), false, false); + assertDoubleConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false); + } +} diff --git a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/RangeTest.java b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/RangeTest.java index 1e6b343fc6015b5e863e2275a3453b25f042a702..20ae203bb634f99fe46241421fde08a4bd345024 100644 --- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/RangeTest.java +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/RangeTest.java @@ -226,116 +226,114 @@ public class RangeTest extends OpTestCase { assertEquals(first, LongStream.iterate(0, i -> i + 1).parallel().filter(i -> i > 10000).findFirst().getAsLong()); } - // Enable when Stream.concat is present and range implementations are - // updated to use that -// private static void assertSizedAndSubSized(Spliterator s) { -// assertTrue(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); -// } -// -// private static void assertNotSizedAndSubSized(Spliterator s) { -// assertFalse(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); -// } -// -// public void testLongLongRange() { -// // Test [Long.MIN_VALUE, Long.MAX_VALUE) -// // This will concatenate streams of three ranges -// // [Long.MIN_VALUE, x) [x, 0) [0, Long.MAX_VALUE) -// // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1 -// { -// Spliterator.OfLong s = LongStream.range(Long.MIN_VALUE, Long.MAX_VALUE).spliterator(); -// -// assertEquals(s.estimateSize(), Long.MAX_VALUE); -// assertNotSizedAndSubSized(s); -// -// Spliterator.OfLong s1 = s.trySplit(); -// assertNotSizedAndSubSized(s1); -// assertSizedAndSubSized(s); -// -// Spliterator.OfLong s2 = s1.trySplit(); -// assertSizedAndSubSized(s1); -// assertSizedAndSubSized(s2); -// -// assertTrue(s.estimateSize() == Long.MAX_VALUE); -// assertTrue(s1.estimateSize() < Long.MAX_VALUE); -// assertTrue(s2.estimateSize() < Long.MAX_VALUE); -// -// assertEquals(s.estimateSize() + s1.estimateSize() + s2.estimateSize(), -// Long.MAX_VALUE - Long.MIN_VALUE); -// } -// -// long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} }; -// for (int i = 0; i < ranges.length; i++) { -// long start = ranges[i][0]; -// long end = ranges[i][1]; -// -// Spliterator.OfLong s = LongStream.range(start, end).spliterator(); -// -// assertEquals(s.estimateSize(), Long.MAX_VALUE); -// assertNotSizedAndSubSized(s); -// -// Spliterator.OfLong s1 = s.trySplit(); -// assertSizedAndSubSized(s1); -// assertSizedAndSubSized(s); -// -// assertTrue(s.estimateSize() < Long.MAX_VALUE); -// assertTrue(s1.estimateSize() < Long.MAX_VALUE); -// -// assertEquals(s.estimateSize() + s1.estimateSize(), end - start); -// } -// } -// -// public void testLongLongRangeClosed() { -// // Test [Long.MIN_VALUE, Long.MAX_VALUE] -// // This will concatenate streams of four ranges -// // [Long.MIN_VALUE, x) [x, 0) [0, y) [y, Long.MAX_VALUE] -// // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1 -// // y = Long.divideUnsigned(Long.MAX_VALUE, 2) + 1 -// -// { -// Spliterator.OfLong s = LongStream.rangeClosed(Long.MIN_VALUE, Long.MAX_VALUE).spliterator(); -// -// assertEquals(s.estimateSize(), Long.MAX_VALUE); -// assertNotSizedAndSubSized(s); -// -// Spliterator.OfLong s1 = s.trySplit(); -// assertNotSizedAndSubSized(s1); -// assertNotSizedAndSubSized(s); -// -// Spliterator.OfLong s2 = s1.trySplit(); -// assertSizedAndSubSized(s1); -// assertSizedAndSubSized(s2); -// -// Spliterator.OfLong s3 = s.trySplit(); -// assertSizedAndSubSized(s3); -// assertSizedAndSubSized(s); -// -// assertTrue(s.estimateSize() < Long.MAX_VALUE); -// assertTrue(s3.estimateSize() < Long.MAX_VALUE); -// assertTrue(s1.estimateSize() < Long.MAX_VALUE); -// assertTrue(s2.estimateSize() < Long.MAX_VALUE); -// -// assertEquals(s.estimateSize() + s3.estimateSize() + s1.estimateSize() + s2.estimateSize(), -// Long.MAX_VALUE - Long.MIN_VALUE + 1); -// } -// -// long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} }; -// for (int i = 0; i < ranges.length; i++) { -// long start = ranges[i][0]; -// long end = ranges[i][1]; -// -// Spliterator.OfLong s = LongStream.rangeClosed(start, end).spliterator(); -// -// assertEquals(s.estimateSize(), Long.MAX_VALUE); -// assertNotSizedAndSubSized(s); -// -// Spliterator.OfLong s1 = s.trySplit(); -// assertSizedAndSubSized(s1); -// assertSizedAndSubSized(s); -// -// assertTrue(s.estimateSize() < Long.MAX_VALUE); -// assertTrue(s1.estimateSize() < Long.MAX_VALUE); -// -// assertEquals(s.estimateSize() + s1.estimateSize(), end - start + 1); -// } -// } + private static void assertSizedAndSubSized(Spliterator s) { + assertTrue(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); + } + + private static void assertNotSizedAndSubSized(Spliterator s) { + assertFalse(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); + } + + public void testLongLongRange() { + // Test [Long.MIN_VALUE, Long.MAX_VALUE) + // This will concatenate streams of three ranges + // [Long.MIN_VALUE, x) [x, 0) [0, Long.MAX_VALUE) + // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1 + { + Spliterator.OfLong s = LongStream.range(Long.MIN_VALUE, Long.MAX_VALUE).spliterator(); + + assertEquals(s.estimateSize(), Long.MAX_VALUE); + assertNotSizedAndSubSized(s); + + Spliterator.OfLong s1 = s.trySplit(); + assertNotSizedAndSubSized(s1); + assertSizedAndSubSized(s); + + Spliterator.OfLong s2 = s1.trySplit(); + assertSizedAndSubSized(s1); + assertSizedAndSubSized(s2); + + assertTrue(s.estimateSize() == Long.MAX_VALUE); + assertTrue(s1.estimateSize() < Long.MAX_VALUE); + assertTrue(s2.estimateSize() < Long.MAX_VALUE); + + assertEquals(s.estimateSize() + s1.estimateSize() + s2.estimateSize(), + Long.MAX_VALUE - Long.MIN_VALUE); + } + + long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} }; + for (int i = 0; i < ranges.length; i++) { + long start = ranges[i][0]; + long end = ranges[i][1]; + + Spliterator.OfLong s = LongStream.range(start, end).spliterator(); + + assertEquals(s.estimateSize(), Long.MAX_VALUE); + assertNotSizedAndSubSized(s); + + Spliterator.OfLong s1 = s.trySplit(); + assertSizedAndSubSized(s1); + assertSizedAndSubSized(s); + + assertTrue(s.estimateSize() < Long.MAX_VALUE); + assertTrue(s1.estimateSize() < Long.MAX_VALUE); + + assertEquals(s.estimateSize() + s1.estimateSize(), end - start); + } + } + + public void testLongLongRangeClosed() { + // Test [Long.MIN_VALUE, Long.MAX_VALUE] + // This will concatenate streams of four ranges + // [Long.MIN_VALUE, x) [x, 0) [0, y) [y, Long.MAX_VALUE] + // where x = Long.divideUnsigned(0 - Long.MIN_VALUE, 2) + 1 + // y = Long.divideUnsigned(Long.MAX_VALUE, 2) + 1 + + { + Spliterator.OfLong s = LongStream.rangeClosed(Long.MIN_VALUE, Long.MAX_VALUE).spliterator(); + + assertEquals(s.estimateSize(), Long.MAX_VALUE); + assertNotSizedAndSubSized(s); + + Spliterator.OfLong s1 = s.trySplit(); + assertNotSizedAndSubSized(s1); + assertNotSizedAndSubSized(s); + + Spliterator.OfLong s2 = s1.trySplit(); + assertSizedAndSubSized(s1); + assertSizedAndSubSized(s2); + + Spliterator.OfLong s3 = s.trySplit(); + assertSizedAndSubSized(s3); + assertSizedAndSubSized(s); + + assertTrue(s.estimateSize() < Long.MAX_VALUE); + assertTrue(s3.estimateSize() < Long.MAX_VALUE); + assertTrue(s1.estimateSize() < Long.MAX_VALUE); + assertTrue(s2.estimateSize() < Long.MAX_VALUE); + + assertEquals(s.estimateSize() + s3.estimateSize() + s1.estimateSize() + s2.estimateSize(), + Long.MAX_VALUE - Long.MIN_VALUE + 1); + } + + long[][] ranges = { {Long.MIN_VALUE, 0}, {-1, Long.MAX_VALUE} }; + for (int i = 0; i < ranges.length; i++) { + long start = ranges[i][0]; + long end = ranges[i][1]; + + Spliterator.OfLong s = LongStream.rangeClosed(start, end).spliterator(); + + assertEquals(s.estimateSize(), Long.MAX_VALUE); + assertNotSizedAndSubSized(s); + + Spliterator.OfLong s1 = s.trySplit(); + assertSizedAndSubSized(s1); + assertSizedAndSubSized(s); + + assertTrue(s.estimateSize() < Long.MAX_VALUE); + assertTrue(s1.estimateSize() < Long.MAX_VALUE); + + assertEquals(s.estimateSize() + s1.estimateSize(), end - start + 1); + } + } }