# HG changeset patch # User redestad # Date 1512786819 -3600 # Sat Dec 09 03:33:39 2017 +0100 # Node ID bf2b4287f3b1939d9dbed8c23582086a09123a44 # Parent db044d7e9885726488bf26a77f4d4d14d7b60807 8193128: Reduce number of implementation classes returned by List/Set/Map.of() 8191418: List.of().indexOf(null) doesn't throw NullPointerException Reviewed-by: smarks, jrose, martin, plevart diff --git a/src/java.base/share/classes/java/util/AbstractSet.java b/src/java.base/share/classes/java/util/AbstractSet.java --- a/src/java.base/share/classes/java/util/AbstractSet.java +++ b/src/java.base/share/classes/java/util/AbstractSet.java @@ -93,9 +93,7 @@ return false; try { return containsAll(c); - } catch (ClassCastException unused) { - return false; - } catch (NullPointerException unused) { + } catch (ClassCastException | NullPointerException unused) { return false; } } diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -72,44 +72,352 @@ // ---------- List Implementations ---------- - abstract static class AbstractImmutableList extends AbstractList - implements RandomAccess, Serializable { + @SuppressWarnings("unchecked") + static List emptyList() { + return (List) ListN.EMPTY_LIST; + } + + static abstract class AbstractImmutableCollection extends AbstractCollection { + // all mutating methods throw UnsupportedOperationException @Override public boolean add(E e) { throw uoe(); } @Override public boolean addAll(Collection c) { throw uoe(); } - @Override public boolean addAll(int index, Collection c) { throw uoe(); } @Override public void clear() { throw uoe(); } @Override public boolean remove(Object o) { throw uoe(); } @Override public boolean removeAll(Collection c) { throw uoe(); } @Override public boolean removeIf(Predicate filter) { throw uoe(); } - @Override public void replaceAll(UnaryOperator operator) { throw uoe(); } @Override public boolean retainAll(Collection c) { throw uoe(); } - @Override public void sort(Comparator c) { throw uoe(); } } - static final class List0 extends AbstractImmutableList { - private static final List0 INSTANCE = new List0<>(); + static abstract class AbstractImmutableSet extends AbstractImmutableCollection + implements Set { - @SuppressWarnings("unchecked") - static List0 instance() { - return (List0) INSTANCE; + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof Set)) { + return false; + } + + Collection c = (Collection) o; + if (c.size() != size()) { + return false; + } + for (Object e : c) { + if (e == null || !contains(e)) { + return false; + } + } + return true; } - private List0() { } + @Override + public abstract int hashCode(); + } + + static abstract class AbstractImmutableList extends AbstractImmutableCollection + implements List, RandomAccess { + + // all mutating methods throw UnsupportedOperationException + @Override public void add(int index, E element) { throw uoe(); } + @Override public boolean addAll(int index, Collection c) { throw uoe(); } + @Override public E remove(int index) { throw uoe(); } + @Override public void replaceAll(UnaryOperator operator) { throw uoe(); } + @Override public E set(int index, E element) { throw uoe(); } + @Override public void sort(Comparator c) { throw uoe(); } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + subListRangeCheck(fromIndex, toIndex, size); + return new SubList(this, fromIndex, toIndex); + } + + private static final class SubList extends AbstractImmutableList + implements RandomAccess { + private final List root; + final int offset; + int size; + + /** + * Constructs a sublist of an arbitrary AbstractList, which is + * not a SubList itself. + */ + SubList(List root, int fromIndex, int toIndex) { + this.root = root; + this.offset = fromIndex; + this.size = toIndex - fromIndex; + } + + /** + * Constructs a sublist of another SubList. + */ + SubList(SubList parent, int fromIndex, int toIndex) { + this.root = parent.root; + this.offset = parent.offset + fromIndex; + this.size = toIndex - fromIndex; + } + + public E get(int index) { + Objects.checkIndex(index, size); + return root.get(offset + index); + } + + public int size() { + return size; + } + + public Iterator iterator() { + return listIterator(); + } + + public ListIterator listIterator(int index) { + rangeCheck(index); + + ListIterator i = root.listIterator(offset + index); + + return new ListIterator<>() { + + public boolean hasNext() { + return nextIndex() < size; + } + + public E next() { + if (hasNext()) { + return i.next(); + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasPrevious() { + return previousIndex() >= 0; + } + + public E previous() { + if (hasPrevious()) { + return i.previous(); + } else { + throw new NoSuchElementException(); + } + } + + public int nextIndex() { + return i.nextIndex() - offset; + } + + public int previousIndex() { + return i.previousIndex() - offset; + } + + public void remove() { throw uoe(); } + public void set(E e) { throw uoe(); } + public void add(E e) { throw uoe(); } + }; + } + + public List subList(int fromIndex, int toIndex) { + subListRangeCheck(fromIndex, toIndex, size); + return new SubList<>(this, fromIndex, toIndex); + } + + private void rangeCheck(int index) { + if (index < 0 || index > size) { + throw outOfBounds(index); + } + } + } + + static void subListRangeCheck(int fromIndex, int toIndex, int size) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > size) + throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) + throw new IllegalArgumentException("fromIndex(" + fromIndex + + ") > toIndex(" + toIndex + ")"); + } + + @Override + public Iterator iterator() { + return new Itr(size()); + } + + @Override + public ListIterator listIterator() { + return listIterator(0); + } + + @Override + public ListIterator listIterator(final int index) { + int size = size(); + if (index < 0 || index > size) { + throw outOfBounds(index); + } + return new ListItr(index, size); + } + + private class Itr implements Iterator { + + int cursor; + + private final int size; + + Itr(int size) { + this.size = size; + } + + public boolean hasNext() { + return cursor != size; + } + + public E next() { + try { + int i = cursor; + E next = get(i); + cursor = i + 1; + return next; + } catch (IndexOutOfBoundsException e) { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw uoe(); + } + } + + private class ListItr extends Itr implements ListIterator { + + ListItr(int index, int size) { + super(size); + cursor = index; + } + + public boolean hasPrevious() { + return cursor != 0; + } + + public E previous() { + try { + int i = cursor - 1; + E previous = get(i); + cursor = i; + return previous; + } catch (IndexOutOfBoundsException e) { + throw new NoSuchElementException(); + } + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public void set(E e) { + throw uoe(); + } + + public void add(E e) { + throw uoe(); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof List)) { + return false; + } + + Iterator oit = ((List) o).iterator(); + for (int i = 0, s = size(); i < s; i++) { + if (!oit.hasNext() || !get(i).equals(oit.next())) { + return false; + } + } + return !oit.hasNext(); + } + + @Override + public int indexOf(Object o) { + Objects.requireNonNull(o); + for (int i = 0, s = size(); i < s; i++) { + if (o.equals(get(i))) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object o) { + Objects.requireNonNull(o); + for (int i = size() - 1; i >= 0; i--) { + if (o.equals(get(i))) { + return i; + } + } + return -1; + } + + @Override + public int hashCode() { + int hash = 1; + for (int i = 0, s = size(); i < s; i++) { + hash = 31 * hash + get(i).hashCode(); + } + return hash; + } + + @Override + public boolean contains(Object o) { + return indexOf(o) >= 0; + } + + IndexOutOfBoundsException outOfBounds(int index) { + return new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); + } + } + + static final class List12 extends AbstractImmutableList + implements Serializable { + + @Stable + private final E e0; + + @Stable + private final E e1; + + List12(E e0) { + this.e0 = Objects.requireNonNull(e0); + this.e1 = null; + } + + List12(E e0, E e1) { + this.e0 = Objects.requireNonNull(e0); + this.e1 = Objects.requireNonNull(e1); + } @Override public int size() { - return 0; + return e1 != null ? 2 : 1; } @Override public E get(int index) { - Objects.checkIndex(index, 0); // always throws IndexOutOfBoundsException - return null; // but the compiler doesn't know this - } - - @Override - public Iterator iterator() { - return Collections.emptyIterator(); + if (index == 0) { + return e0; + } else if (index == 1 && e1 != null) { + return e1; + } + throw outOfBounds(index); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -117,111 +425,20 @@ } private Object writeReplace() { - return new CollSer(CollSer.IMM_LIST); - } - - @Override - public boolean contains(Object o) { - Objects.requireNonNull(o); - return false; - } - - @Override - public boolean containsAll(Collection o) { - return o.isEmpty(); // implicit nullcheck of o - } - - @Override - public int hashCode() { - return 1; - } - } - - static final class List1 extends AbstractImmutableList { - @Stable - private final E e0; - - List1(E e0) { - this.e0 = Objects.requireNonNull(e0); - } - - @Override - public int size() { - return 1; - } - - @Override - public E get(int index) { - Objects.checkIndex(index, 1); - return e0; - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new InvalidObjectException("not serial proxy"); - } - - private Object writeReplace() { - return new CollSer(CollSer.IMM_LIST, e0); - } - - @Override - public boolean contains(Object o) { - return o.equals(e0); // implicit nullcheck of o - } - - @Override - public int hashCode() { - return 31 + e0.hashCode(); - } - } - - static final class List2 extends AbstractImmutableList { - @Stable - private final E e0; - @Stable - private final E e1; - - List2(E e0, E e1) { - this.e0 = Objects.requireNonNull(e0); - this.e1 = Objects.requireNonNull(e1); - } - - @Override - public int size() { - return 2; - } - - @Override - public E get(int index) { - Objects.checkIndex(index, 2); - if (index == 0) { - return e0; - } else { // index == 1 - return e1; + if (e1 == null) { + return new CollSer(CollSer.IMM_LIST, e0); + } else { + return new CollSer(CollSer.IMM_LIST, e0, e1); } } - @Override - public boolean contains(Object o) { - return o.equals(e0) || o.equals(e1); // implicit nullcheck of o - } - - @Override - public int hashCode() { - int hash = 31 + e0.hashCode(); - return 31 * hash + e1.hashCode(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new InvalidObjectException("not serial proxy"); - } - - private Object writeReplace() { - return new CollSer(CollSer.IMM_LIST, e0, e1); - } } - static final class ListN extends AbstractImmutableList { + static final class ListN extends AbstractImmutableList + implements Serializable { + + static final List EMPTY_LIST = new ListN<>(); + @Stable private final E[] elements; @@ -233,7 +450,12 @@ for (int i = 0; i < input.length; i++) { tmp[i] = Objects.requireNonNull(input[i]); } - this.elements = tmp; + elements = tmp; + } + + @Override + public boolean isEmpty() { + return size() == 0; } @Override @@ -243,29 +465,9 @@ @Override public E get(int index) { - Objects.checkIndex(index, elements.length); return elements[index]; } - @Override - public boolean contains(Object o) { - for (E e : elements) { - if (o.equals(e)) { // implicit nullcheck of o - return true; - } - } - return false; - } - - @Override - public int hashCode() { - int hash = 1; - for (E e : elements) { - hash = 31 * hash + e.hashCode(); - } - return hash; - } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("not serial proxy"); } @@ -277,105 +479,25 @@ // ---------- Set Implementations ---------- - abstract static class AbstractImmutableSet extends AbstractSet implements Serializable { - @Override public boolean add(E e) { throw uoe(); } - @Override public boolean addAll(Collection c) { throw uoe(); } - @Override public void clear() { throw uoe(); } - @Override public boolean remove(Object o) { throw uoe(); } - @Override public boolean removeAll(Collection c) { throw uoe(); } - @Override public boolean removeIf(Predicate filter) { throw uoe(); } - @Override public boolean retainAll(Collection c) { throw uoe(); } + @SuppressWarnings("unchecked") + static Set emptySet() { + return (Set) SetN.EMPTY_SET; } - static final class Set0 extends AbstractImmutableSet { - private static final Set0 INSTANCE = new Set0<>(); + static final class Set12 extends AbstractImmutableSet + implements Serializable { - @SuppressWarnings("unchecked") - static Set0 instance() { - return (Set0) INSTANCE; - } - - private Set0() { } - - @Override - public int size() { - return 0; - } - - @Override - public boolean contains(Object o) { - Objects.requireNonNull(o); - return false; - } - - @Override - public boolean containsAll(Collection o) { - return o.isEmpty(); // implicit nullcheck of o - } - - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new InvalidObjectException("not serial proxy"); - } - - private Object writeReplace() { - return new CollSer(CollSer.IMM_SET); - } - - @Override - public int hashCode() { - return 0; - } - } - - static final class Set1 extends AbstractImmutableSet { - @Stable - private final E e0; - - Set1(E e0) { - this.e0 = Objects.requireNonNull(e0); - } - - @Override - public int size() { - return 1; - } - - @Override - public boolean contains(Object o) { - return o.equals(e0); // implicit nullcheck of o - } - - @Override - public Iterator iterator() { - return Collections.singletonIterator(e0); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new InvalidObjectException("not serial proxy"); - } - - private Object writeReplace() { - return new CollSer(CollSer.IMM_SET, e0); - } - - @Override - public int hashCode() { - return e0.hashCode(); - } - } - - static final class Set2 extends AbstractImmutableSet { @Stable final E e0; @Stable final E e1; - Set2(E e0, E e1) { + Set12(E e0) { + this.e0 = Objects.requireNonNull(e0); + this.e1 = null; + } + + Set12(E e0, E e1) { if (e0.equals(Objects.requireNonNull(e1))) { // implicit nullcheck of e0 throw new IllegalArgumentException("duplicate element: " + e0); } @@ -391,7 +513,7 @@ @Override public int size() { - return 2; + return (e1 == null) ? 1 : 2; } @Override @@ -401,26 +523,26 @@ @Override public int hashCode() { - return e0.hashCode() + e1.hashCode(); + return e0.hashCode() + (e1 == null ? 0 : e1.hashCode()); } @Override public Iterator iterator() { - return new Iterator() { - private int idx = 0; + return new Iterator<>() { + private int idx = size(); @Override public boolean hasNext() { - return idx < 2; + return idx > 0; } @Override public E next() { - if (idx == 0) { + if (idx == 1) { + idx = 0; + return e0; + } else if (idx == 2) { idx = 1; - return e0; - } else if (idx == 1) { - idx = 2; return e1; } else { throw new NoSuchElementException(); @@ -434,7 +556,11 @@ } private Object writeReplace() { - return new CollSer(CollSer.IMM_SET, e0, e1); + if (e1 == null) { + return new CollSer(CollSer.IMM_SET, e0); + } else { + return new CollSer(CollSer.IMM_SET, e0, e1); + } } } @@ -444,7 +570,11 @@ * least one null is always present. * @param the element type */ - static final class SetN extends AbstractImmutableSet { + static final class SetN extends AbstractImmutableSet + implements Serializable { + + static final Set EMPTY_SET = new SetN<>(); + @Stable final E[] elements; @Stable @@ -474,12 +604,13 @@ @Override public boolean contains(Object o) { - return probe(o) >= 0; // implicit nullcheck of o + Objects.requireNonNull(o); + return size > 0 && probe(o) >= 0; } @Override public Iterator iterator() { - return new Iterator() { + return new Iterator<>() { private int idx = 0; @Override @@ -549,6 +680,11 @@ // ---------- Map Implementations ---------- + @SuppressWarnings("unchecked") + static Map emptyMap() { + return (Map) MapN.EMPTY_MAP; + } + abstract static class AbstractImmutableMap extends AbstractMap implements Serializable { @Override public void clear() { throw uoe(); } @Override public V compute(K key, BiFunction rf) { throw uoe(); } @@ -565,47 +701,6 @@ @Override public void replaceAll(BiFunction f) { throw uoe(); } } - static final class Map0 extends AbstractImmutableMap { - private static final Map0 INSTANCE = new Map0<>(); - - @SuppressWarnings("unchecked") - static Map0 instance() { - return (Map0) INSTANCE; - } - - private Map0() { } - - @Override - public Set> entrySet() { - return Set.of(); - } - - @Override - public boolean containsKey(Object o) { - Objects.requireNonNull(o); - return false; - } - - @Override - public boolean containsValue(Object o) { - Objects.requireNonNull(o); - return false; - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new InvalidObjectException("not serial proxy"); - } - - private Object writeReplace() { - return new CollSer(CollSer.IMM_MAP); - } - - @Override - public int hashCode() { - return 0; - } - } - static final class Map1 extends AbstractImmutableMap { @Stable private final K k0; @@ -656,8 +751,12 @@ * @param the value type */ static final class MapN extends AbstractImmutableMap { + + static final Map EMPTY_MAP = new MapN<>(); + @Stable final Object[] table; // pairs of key, value + @Stable final int size; // number of pairs @@ -689,14 +788,16 @@ @Override public boolean containsKey(Object o) { - return probe(o) >= 0; // implicit nullcheck of o + Objects.requireNonNull(o); + return size > 0 && probe(o) >= 0; } @Override public boolean containsValue(Object o) { + Objects.requireNonNull(o); for (int i = 1; i < table.length; i += 2) { Object v = table[i]; - if (v != null && o.equals(v)) { // implicit nullcheck of o + if (v != null && o.equals(v)) { return true; } } @@ -718,6 +819,10 @@ @Override @SuppressWarnings("unchecked") public V get(Object o) { + if (size == 0) { + Objects.requireNonNull(o); + return null; + } int i = probe(o); if (i >= 0) { return (V)table[i+1]; @@ -733,7 +838,7 @@ @Override public Set> entrySet() { - return new AbstractSet>() { + return new AbstractSet<>() { @Override public int size() { return MapN.this.size; @@ -741,7 +846,7 @@ @Override public Iterator> iterator() { - return new Iterator>() { + return new Iterator<>() { int idx = 0; @Override @@ -948,7 +1053,7 @@ return Set.of(array); case IMM_MAP: if (array.length == 0) { - return ImmutableCollections.Map0.instance(); + return ImmutableCollections.emptyMap(); } else if (array.length == 2) { return new ImmutableCollections.Map1<>(array[0], array[1]); } else { diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -788,7 +788,7 @@ * @since 9 */ static List of() { - return ImmutableCollections.List0.instance(); + return ImmutableCollections.emptyList(); } /** @@ -804,7 +804,7 @@ * @since 9 */ static List of(E e1) { - return new ImmutableCollections.List1<>(e1); + return new ImmutableCollections.List12<>(e1); } /** @@ -821,7 +821,7 @@ * @since 9 */ static List of(E e1, E e2) { - return new ImmutableCollections.List2<>(e1, e2); + return new ImmutableCollections.List12<>(e1, e2); } /** @@ -1031,11 +1031,11 @@ static List of(E... elements) { switch (elements.length) { // implicit null check of elements case 0: - return ImmutableCollections.List0.instance(); + return ImmutableCollections.emptyList(); case 1: - return new ImmutableCollections.List1<>(elements[0]); + return new ImmutableCollections.List12<>(elements[0]); case 2: - return new ImmutableCollections.List2<>(elements[0], elements[1]); + return new ImmutableCollections.List12<>(elements[0], elements[1]); default: return new ImmutableCollections.ListN<>(elements); } diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.java @@ -1287,7 +1287,7 @@ * @since 9 */ static Map of() { - return ImmutableCollections.Map0.instance(); + return ImmutableCollections.emptyMap(); } /** @@ -1604,11 +1604,11 @@ @SuppressWarnings("varargs") static Map ofEntries(Entry... entries) { if (entries.length == 0) { // implicit null check of entries array - return ImmutableCollections.Map0.instance(); + return ImmutableCollections.emptyMap(); } else if (entries.length == 1) { // implicit null check of the array slot return new ImmutableCollections.Map1<>(entries[0].getKey(), - entries[0].getValue()); + entries[0].getValue()); } else { Object[] kva = new Object[entries.length << 1]; int a = 0; diff --git a/src/java.base/share/classes/java/util/Set.java b/src/java.base/share/classes/java/util/Set.java --- a/src/java.base/share/classes/java/util/Set.java +++ b/src/java.base/share/classes/java/util/Set.java @@ -449,7 +449,7 @@ * @since 9 */ static Set of() { - return ImmutableCollections.Set0.instance(); + return ImmutableCollections.emptySet(); } /** @@ -464,7 +464,7 @@ * @since 9 */ static Set of(E e1) { - return new ImmutableCollections.Set1<>(e1); + return new ImmutableCollections.Set12<>(e1); } /** @@ -481,7 +481,7 @@ * @since 9 */ static Set of(E e1, E e2) { - return new ImmutableCollections.Set2<>(e1, e2); + return new ImmutableCollections.Set12<>(e1, e2); } /** @@ -692,11 +692,11 @@ static Set of(E... elements) { switch (elements.length) { // implicit null check of elements case 0: - return ImmutableCollections.Set0.instance(); + return ImmutableCollections.emptySet(); case 1: - return new ImmutableCollections.Set1<>(elements[0]); + return new ImmutableCollections.Set12<>(elements[0]); case 2: - return new ImmutableCollections.Set2<>(elements[0], elements[1]); + return new ImmutableCollections.Set12<>(elements[0], elements[1]); default: return new ImmutableCollections.SetN<>(elements); } diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java --- a/test/jdk/java/util/Collection/MOAT.java +++ b/test/jdk/java/util/Collection/MOAT.java @@ -26,7 +26,7 @@ * @bug 6207984 6272521 6192552 6269713 6197726 6260652 5073546 4137464 * 4155650 4216399 4294891 6282555 6318622 6355327 6383475 6420753 * 6431845 4802633 6570566 6570575 6570631 6570924 6691185 6691215 - * 4802647 7123424 8024709 + * 4802647 7123424 8024709 8193128 * @summary Run many tests on many Collection and Map implementations * @author Martin Buchholz * @modules java.base/java.util:open @@ -212,8 +212,11 @@ // Immutable List testEmptyList(List.of()); + testEmptyList(List.of().subList(0,0)); testListMutatorsAlwaysThrow(List.of()); + testListMutatorsAlwaysThrow(List.of().subList(0,0)); testEmptyListMutatorsAlwaysThrow(List.of()); + testEmptyListMutatorsAlwaysThrow(List.of().subList(0,0)); for (List list : Arrays.asList( List.of(), List.of(1), @@ -230,6 +233,17 @@ testCollection(list); testImmutableList(list); testListMutatorsAlwaysThrow(list); + if (list.size() >= 1) { + // test subLists + List headList = list.subList(0, list.size() - 1); + List tailList = list.subList(1, list.size()); + testCollection(headList); + testCollection(tailList); + testImmutableList(headList); + testImmutableList(tailList); + testListMutatorsAlwaysThrow(headList); + testListMutatorsAlwaysThrow(tailList); + } } List listCopy = List.copyOf(Arrays.asList(1, 2, 3)); @@ -243,6 +257,44 @@ testImmutableList(listCollected); testListMutatorsAlwaysThrow(listCollected); + // List indexOf / lastIndexOf + + // 0 element + System.out.println("testListIndexOf size 0"); + testListIndexOf(-1, -1); + + System.out.println("testListIndexOf size 1"); + testListIndexOf(-1, -1, 0); + testListIndexOf(0, 0, 1); + + System.out.println("testListIndexOf size 2"); + testListIndexOf(-1, -1, 0, 0); + testListIndexOf(0, 0, 1, 0); + testListIndexOf(0, 1, 1, 1); + testListIndexOf(1, 1, 0, 1); + + + System.out.println("testListIndexOf size 3"); + testListIndexOf(-1, -1, 0, 0, 0); + testListIndexOf(0, 0, 1, 0, 0); + testListIndexOf(0, 1, 1, 1, 0); + testListIndexOf(1, 2, 0, 1, 1); + testListIndexOf(2, 2, 0, 0, 1); + + System.out.println("testListIndexOf size N"); + testListIndexOf(-1, -1, 0, 0, 0, 0, 0, 0, 0); + testListIndexOf(2, 6, 0, 0, 1, 0, 1, 0, 1); + testListIndexOf(4, 4, 0, 0, 0, 0, 1, 0, 0); + testListIndexOf(0, 6, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 7, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(0, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + testListIndexOf(12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + testListIndexOf(-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + // Immutable Set testEmptySet(Set.of()); testCollMutatorsAlwaysThrow(Set.of()); @@ -963,6 +1015,37 @@ equal(it.next(), 4); } + // for any array of integer values, check that the result of lastIndexOf(1) + // and indexOf(1) match assumptions for all types of List we can + // construct + private static void testListIndexOf(final int index, + final int lastIndex, + final Integer ... values) { + if (values.length == 0) { + checkListIndexOf(emptyList(), index, lastIndex); + } else if (values.length == 1) { + checkListIndexOf(singletonList(values[0]), index, lastIndex); + checkListIndexOf(nCopies(25, values[0]), index, lastIndex == 0 ? 24 : -1); + } + List l = List.of(values); + checkListIndexOf(l, index, lastIndex); + checkListIndexOf(Arrays.asList(values), index, lastIndex); + checkListIndexOf(new ArrayList(l), index, lastIndex); + checkListIndexOf(new LinkedList(l), index, lastIndex); + checkListIndexOf(new Vector(l), index, lastIndex); + checkListIndexOf(new CopyOnWriteArrayList(l), index, lastIndex); + } + + private static void checkListIndexOf(final List list, + final int index, + final int lastIndex) { + String msg = list.getClass().toString(); + equal(list.indexOf(1), index, msg); + equal(list.lastIndexOf(1), lastIndex, msg); + equal(list.subList(0, list.size()).indexOf(1), index, msg); + equal(list.subList(0, list.size()).lastIndexOf(1), lastIndex, msg); + } + private static void testList(final List l) { //---------------------------------------------------------------- // 4802633: (coll) AbstractList.addAll(-1,emptyCollection) @@ -1629,6 +1712,9 @@ static void equal(Object x, Object y) { if (x == null ? y == null : x.equals(y)) pass(); else {System.out.println(x + " not equal to " + y); fail();}} + static void equal(Object x, Object y, String msg) { + if (x == null ? y == null : x.equals(y)) pass(); + else {System.out.println(x + " not equal to " + y + " : " + msg); fail();}} static void equal2(Object x, Object y) {equal(x, y); equal(y, x);} public static void main(String[] args) throws Throwable { try { realMain(args); } catch (Throwable t) { unexpected(t); } diff --git a/test/jdk/java/util/Collection/SetFactories.java b/test/jdk/java/util/Collection/SetFactories.java --- a/test/jdk/java/util/Collection/SetFactories.java +++ b/test/jdk/java/util/Collection/SetFactories.java @@ -266,6 +266,11 @@ Set.of((Object[])null); } + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void containsNullShouldThrowNPE(Set act, Set exp) { + act.contains(null); + } + @Test(dataProvider="all") public void serialEquality(Set act, Set exp) { // assume that act.equals(exp) tested elsewhere diff --git a/test/jdk/java/util/List/ListFactories.java b/test/jdk/java/util/List/ListFactories.java --- a/test/jdk/java/util/List/ListFactories.java +++ b/test/jdk/java/util/List/ListFactories.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -72,7 +73,7 @@ @DataProvider(name="empty") public Iterator empty() { return Collections.singletonList( - a(List.of(), Collections.emptyList()) + a(List.of(), asList()) ).iterator(); } @@ -104,11 +105,50 @@ ).iterator(); } + @DataProvider(name="sublists") + public Iterator sublists() { + return asList( + a(List.of().subList(0,0), + asList()), + a(List.of("a").subList(0,0), + asList("a").subList(0,0)), + a(List.of("a", "b").subList(0,1), + asList("a", "b").subList(0,1)), + a(List.of("a", "b", "c").subList(1,3), + asList("a", "b", "c").subList(1,3)), + a(List.of("a", "b", "c", "d").subList(0,4), + asList("a", "b", "c", "d").subList(0,4)), + a(List.of("a", "b", "c", "d", "e").subList(0,3), + asList("a", "b", "c", "d", "e").subList(0,3)), + a(List.of("a", "b", "c", "d", "e", "f").subList(3, 5), + asList("a", "b", "c", "d", "e", "f").subList(3, 5)), + a(List.of("a", "b", "c", "d", "e", "f", "g").subList(0, 7), + asList("a", "b", "c", "d", "e", "f", "g").subList(0, 7)), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h").subList(0, 0), + asList("a", "b", "c", "d", "e", "f", "g", "h").subList(0, 0)), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i").subList(4, 5), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i").subList(4, 5)), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j").subList(1,10), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j").subList(1,10)), + a(List.of(stringArray).subList(5, NUM_STRINGS), + asList(Arrays.copyOfRange(stringArray, 5, NUM_STRINGS))) + ).iterator(); + } + @DataProvider(name="all") public Iterator all() { List all = new ArrayList<>(); empty().forEachRemaining(all::add); nonempty().forEachRemaining(all::add); + sublists().forEachRemaining(all::add); + return all.iterator(); + } + + @DataProvider(name="nonsublists") + public Iterator nonsublists() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); return all.iterator(); } @@ -212,7 +252,62 @@ assertEquals(list, Arrays.asList(stringArray)); } - @Test(dataProvider="all") + @Test + public void indexOf() { + assertEquals(List.of("a").indexOf("a"), 0); + assertEquals(List.of("a", "a").indexOf("a"), 0); + assertEquals(List.of("b", "a", "a").indexOf("a"), 1); + assertEquals(List.of("b", "b", "a", "a").indexOf("a"), 2); + assertEquals(List.of("b", "b", "b", "a", "a").indexOf("a"), 3); + assertEquals(List.of("b", "b", "b", "b", "a", "a").indexOf("a"), 4); + + assertEquals(List.of("a").subList(0, 1).indexOf("a"), 0); + assertEquals(List.of("a", "a").subList(0, 2).indexOf("a"), 0); + assertEquals(List.of("b", "a", "a").subList(0, 3).indexOf("a"), 1); + assertEquals(List.of("b", "b", "a", "a").subList(0, 4).indexOf("a"), 2); + assertEquals(List.of("b", "b", "b", "a", "a").subList(0, 5).indexOf("a"), 3); + assertEquals(List.of("b", "b", "b", "b", "a", "a").subList(0, 6).indexOf("a"), 4); + + assertEquals(List.of("a").lastIndexOf("a"), 0); + assertEquals(List.of("a", "a").lastIndexOf("a"), 1); + assertEquals(List.of("b", "a", "a").lastIndexOf("a"), 2); + assertEquals(List.of("b", "b", "a", "a").lastIndexOf("a"), 3); + assertEquals(List.of("b", "b", "b", "a", "a").lastIndexOf("a"), 4); + assertEquals(List.of("b", "b", "b", "b", "a", "a").lastIndexOf("a"), 5); + assertEquals(List.of("c", "b", "b", "b", "a", "a").lastIndexOf("c"), 0); + + assertEquals(List.of("a").subList(0, 1).lastIndexOf("a"), 0); + assertEquals(List.of("a", "a").subList(0, 2).lastIndexOf("a"), 1); + assertEquals(List.of("b", "a", "a").subList(0, 3).lastIndexOf("a"), 2); + assertEquals(List.of("b", "b", "a", "a").subList(0, 4).lastIndexOf("a"), 3); + assertEquals(List.of("b", "b", "b", "a", "a").subList(0, 5).lastIndexOf("a"), 4); + assertEquals(List.of("b", "b", "b", "b", "a", "a").subList(0, 6).lastIndexOf("a"), 5); + assertEquals(List.of("c", "b", "b", "b", "a", "a").subList(0, 6).lastIndexOf("c"), 0); + } + + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void containsNullShouldThrowNPE(List act, List exp) { + act.contains(null); + } + + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void indexOfNullShouldThrowNPE(List act, List exp) { + act.indexOf(null); + } + + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void lastIndexOfNullShouldThrowNPE(List act, List exp) { + act.lastIndexOf(null); + } + + // List.of().subList views should not be Serializable + @Test(dataProvider="sublists") + public void isNotSerializable(List act, List exp) { + assertFalse(act instanceof Serializable); + } + + // ... but List.of() should be + @Test(dataProvider="nonsublists") public void serialEquality(List act, List exp) { // assume that act.equals(exp) tested elsewhere List copy = serialClone(act); diff --git a/test/jdk/java/util/Map/MapFactories.java b/test/jdk/java/util/Map/MapFactories.java --- a/test/jdk/java/util/Map/MapFactories.java +++ b/test/jdk/java/util/Map/MapFactories.java @@ -376,6 +376,16 @@ Map.ofEntries((Map.Entry[])null); } + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void containsValueNullShouldThrowNPE(Map act, Map exp) { + act.containsValue(null); + } + + @Test(dataProvider="all", expectedExceptions=NullPointerException.class) + public void containsKeyNullShouldThrowNPE(Map act, Map exp) { + act.containsKey(null); + } + @Test(dataProvider="all") public void serialEquality(Map act, Map exp) { // assume that act.equals(exp) tested elsewhere