--- old/src/share/classes/java/lang/Iterable.java 2012-12-10 21:19:05.395766019 -0800 +++ new/src/share/classes/java/lang/Iterable.java 2012-12-10 21:19:05.227766014 -0800 @@ -26,6 +26,8 @@ package java.lang; import java.util.Iterator; +import java.util.Objects; +import java.util.function.Block; /** * Implementing this interface allows an object to be the target of @@ -43,4 +45,18 @@ * @return an Iterator. */ Iterator iterator(); + + /** + * Execute the specified Block for each element + * + * @param block The Block to which elements will be provided + * @throws NullPointerException if the specified block is null + * @since 1.8 + */ + public default void forEach(Block block) { + Objects.requireNonNull(block); + for (T t : this) { + block.accept(t); + } + } } --- old/src/share/classes/java/util/ArrayList.java 2012-12-10 21:19:06.163766049 -0800 +++ new/src/share/classes/java/util/ArrayList.java 2012-12-10 21:19:05.991766043 -0800 @@ -25,6 +25,10 @@ package java.util; +import java.util.function.Block; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + /** * Resizable-array implementation of the List interface. Implements * all optional list operations, and permits all elements, including @@ -1128,4 +1132,90 @@ throw new ConcurrentModificationException(); } } + + @Override + public void forEach(Block block) { + Objects.requireNonNull(block); + final int expectedModCount = modCount; + final E[] elementData = (E[]) this.elementData; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + block.accept(elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean removeAll(Predicate filter) { + Objects.requireNonNull(filter); + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final BitSet removeSet = new BitSet(size); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + @SuppressWarnings("unchecked") + final E element = (E) elementData[i]; + if (filter.test(element)) { + removeSet.set(i); + removeCount++; + } + } + + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + // shift surviving elements left over the spaces left by removed elements + final boolean anyToRemove = removeCount > 0; + if (anyToRemove) { + final int newSize = size - removeCount; + for (int i=0, j=0; modCount == expectedModCount && + (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); + elementData[j] = elementData[i]; + } + for (int k=newSize; modCount == expectedModCount && k < size; k++) { + elementData[k] = null; // Let gc do its work + } + this.size = newSize; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + return anyToRemove; + } + + @Override + @SuppressWarnings("unchecked") + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.operate((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + @SuppressWarnings("unchecked") + public void sort(Comparator c) { + Objects.requireNonNull(c); + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, size, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } } --- old/src/share/classes/java/util/Collection.java 2012-12-10 21:19:06.971766079 -0800 +++ new/src/share/classes/java/util/Collection.java 2012-12-10 21:19:06.799766074 -0800 @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Predicate; + /** * The root interface in the collection hierarchy. A collection * represents a group of objects, known as its elements. Some @@ -453,4 +455,28 @@ * @see Object#equals(Object) */ int hashCode(); + + /** + * Removes all of the elements of this collection which match the provided + * predicate. + * + * @param filter a predicate which returns {@code true} for elements to be + * removed + * @return {@code true} if any elements were removed + * @throws NullPointerException if the specified predicate is null + * @since 1.8 + */ + public default boolean removeAll(Predicate filter) { + Objects.requireNonNull(filter); + boolean removed = false; + Iterator each = iterator(); + while (each.hasNext()) { + if (filter.test(each.next())) { + each.remove(); + removed = true; + } + } + + return removed; + } } --- old/src/share/classes/java/util/Collections.java 2012-12-10 21:19:07.727766109 -0800 +++ new/src/share/classes/java/util/Collections.java 2012-12-10 21:19:07.555766102 -0800 @@ -28,6 +28,9 @@ import java.io.ObjectOutputStream; import java.io.IOException; import java.lang.reflect.Array; +import java.util.function.Block; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; /** * This class consists exclusively of static methods that operate on or return @@ -1107,6 +1110,9 @@ public void clear() { throw new UnsupportedOperationException(); } + public boolean removeAll(Predicate filter) { + throw new UnsupportedOperationException(); + } } /** @@ -1237,6 +1243,12 @@ public boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } + public void replaceAll(UnaryOperator operator) { + throw new UnsupportedOperationException(); + } + public void sort(Comparator c) { + throw new UnsupportedOperationException(); + } public ListIterator listIterator() {return listIterator(0);} public ListIterator listIterator(final int index) { @@ -1678,6 +1690,12 @@ private void writeObject(ObjectOutputStream s) throws IOException { synchronized (mutex) {s.defaultWriteObject();} } + public void forEach(Block block) { + synchronized (mutex) {c.forEach(block);} + } + public boolean removeAll(Predicate filter) { + synchronized (mutex) {return c.removeAll(filter);} + } } /** @@ -1916,6 +1934,12 @@ public boolean addAll(int index, Collection c) { synchronized (mutex) {return list.addAll(index, c);} } + public void replaceAll(UnaryOperator operator) { + synchronized (mutex) {list.replaceAll(operator);} + } + public void sort(Comparator c) { + synchronized (mutex) {list.sort(c);} + } public ListIterator listIterator() { return list.listIterator(); // Must be manually synched by user --- old/src/share/classes/java/util/List.java 2012-12-10 21:19:08.575766140 -0800 +++ new/src/share/classes/java/util/List.java 2012-12-10 21:19:08.411766135 -0800 @@ -25,6 +25,8 @@ package java.util; +import java.util.function.UnaryOperator; + /** * An ordered collection (also known as a sequence). The user of this * interface has precise control over where in the list each element is @@ -597,4 +599,33 @@ * fromIndex > toIndex) */ List subList(int fromIndex, int toIndex); + + /** + * Apply the specified operator to each element of this list, replacing + * the element with the result of applying the operator to the current + * element. + * + * @param operator the operator to apply to each element + * @throws NullPointerException if the specified operator is null + * @since 1.8 + */ + public default void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final ListIterator li = this.listIterator(); + while (li.hasNext()) { + li.set(operator.operate(li.next())); + } + } + + /** + * Sort this list using the supplied {@code Comparator} to compare elements. + * + * @param c the {@code Comparator} used to compare list elements + * @throws NullPointerException if the specified comparator is null + * @since 1.8 + */ + public default void sort(Comparator c) { + Objects.requireNonNull(c); + Collections.sort(this, c); + } } --- old/src/share/classes/java/util/Vector.java 2012-12-10 21:19:09.343766169 -0800 +++ new/src/share/classes/java/util/Vector.java 2012-12-10 21:19:09.171766164 -0800 @@ -25,6 +25,10 @@ package java.util; +import java.util.function.Block; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + /** * The {@code Vector} class implements a growable array of * objects. Like an array, it contains components that can be @@ -1209,4 +1213,91 @@ lastRet = -1; } } + + @Override + public synchronized void forEach(Block block) { + Objects.requireNonNull(block); + final E[] elementData = (E[]) this.elementData; + final int expectedModCount = modCount; + final int size = elementCount; + for (int i=0; modCount == expectedModCount && i < size; i++) { + block.accept(elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + @SuppressWarnings("unchecked") + public synchronized boolean removeAll(Predicate filter) { + Objects.requireNonNull(filter); + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final int size = elementCount; + final BitSet removeSet = new BitSet(size); + final int expectedModCount = modCount; + for (int i=0; modCount == expectedModCount && i < size; i++) { + @SuppressWarnings("unchecked") + final E element = (E) elementData[i]; + if (filter.test(element)) { + removeSet.set(i); + removeCount++; + } + } + + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + // shift surviving elements left over the spaces left by removed elements + final boolean anyToRemove = removeCount > 0; + if (anyToRemove) { + final int newSize = size - removeCount; + for (int i=0, j=0; modCount == expectedModCount && + (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); + elementData[j] = elementData[i]; + } + for (int k=newSize; modCount == expectedModCount && k < size; k++) { + elementData[k] = null; // Let gc do its work + } + elementCount = newSize; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + return anyToRemove; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = elementCount; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.operate((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized void sort(Comparator c) { + Objects.requireNonNull(c); + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, elementCount, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } } --- old/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java 2012-12-10 21:19:10.127766199 -0800 +++ new/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java 2012-12-10 21:19:09.963766193 -0800 @@ -36,6 +36,9 @@ package java.util.concurrent; import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Block; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; /** * A thread-safe variant of {@link java.util.ArrayList} in which all mutative @@ -1317,6 +1320,92 @@ } } + @SuppressWarnings("unchecked") + public void forEach(Block block) { + Objects.requireNonNull(block); + final Object[] elements = getArray(); + for (final Object element : elements) { + block.accept((E) element); + } + } + + @Override + public void sort(Comparator c) { + Objects.requireNonNull(c); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + @SuppressWarnings("unchecked") + final E[] elements = (E[]) getArray(); + final E[] newElements = Arrays.copyOf(elements, elements.length); + Arrays.sort(newElements, c); + setArray(newElements); + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeAll(Predicate filter) { + Objects.requireNonNull(filter); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + @SuppressWarnings("unchecked") + final E[] elements = (E[]) getArray(); + final int size = elements.length; + + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final BitSet removeSet = new BitSet(size); + for (int i=0; i < size; i++) { + final E element = elements[i]; + if (filter.test(element)) { + removeSet.set(i); + removeCount++; + } + } + + // copy surviving elements into a new array + final boolean anyToRemove = removeCount > 0; + if (anyToRemove) { + final int newSize = size - removeCount; + final Object[] newElements = new Object[newSize]; + for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); + newElements[j] = elements[i]; + } + setArray(newElements); + } + + return anyToRemove; + } finally { + lock.unlock(); + } + } + + @Override + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + @SuppressWarnings("unchecked") + final E[] elements = (E[]) getArray(); + final int len = elements.length; + @SuppressWarnings("unchecked") + final E[] newElements = (E[]) new Object[len]; + for (int i=0; i < len; i++) { + newElements[i] = operator.operate(elements[i]); + } + setArray(newElements); + } finally { + lock.unlock(); + } + } + // Support for resetting lock while deserializing private void resetLock() { UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock()); --- /dev/null 2012-12-10 07:41:39.297440197 -0800 +++ new/test/java/util/CollectionExtensionMethods/CollectionExtensionMethodsTest.java 2012-12-10 21:19:10.751766224 -0800 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.function.Predicate; + +/** + * @test + * @library testlibrary + * @build CollectionAsserts CollectionSupplier + * @run testng CollectionExtensionMethodsTest + * @summary Unit tests for extension methods on Collection + */ +public class CollectionExtensionMethodsTest { + + private static final Predicate P_EVEN = x -> 0 == x % 2; + private static final Predicate P_ODD = x -> 0 != x % 2; + + private static final String[] SET_CLASSES = { + "java.util.HashSet", + "java.util.LinkedHashSet", + "java.util.TreeSet" + }; + + @Test + public void testUnmodifiable() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, 10); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set set = ((Set) test.collection); + final Set unmodifiable = Collections.unmodifiableSet(set); + // forEach does not modify, it should not throw UnsupportedOperationException + unmodifiable.forEach((x) -> {}); + try { + unmodifiable.removeAll(P_EVEN); + fail("removeAll on unmodifiable set did not throw exception"); + } catch (UnsupportedOperationException ignore) {} + } + } + + @Test + public void testForNullPointerException() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, 10); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set set = ((Set) test.collection); + try { + set.forEach(null); + fail("forEach with null Block did not throw NPE"); + } catch (NullPointerException nx) {} + try { + set.removeAll((Predicate) null); + fail("removeAll with null Predicate did not throw NPE"); + } catch (NullPointerException nx) {} + } + } + + @Test + public void testForEach() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, 100); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set original = ((Set) test.original); + final Set set = ((Set) test.collection); + final List actual = new LinkedList<>(); + set.forEach(actual::add); + if (test.className.equals("java.util.HashSet")) { + CollectionAsserts.assertContentsUnordered(actual, set); + CollectionAsserts.assertContentsUnordered(actual, original); + } else { + CollectionAsserts.assertContents(actual, set); + CollectionAsserts.assertContents(actual, original); + } + } + } + + @Test + public void testRemoveAll() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, 100); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set original = ((Set) test.original); + final Set set = ((Set) test.collection); + set.removeAll(P_EVEN); + for (final int i : set) { + assertTrue((i % 2) == 1); + } + for (final int i : original) { + if (i % 2 != 0) { + assertTrue(set.contains(i)); + } + } + set.removeAll(P_ODD); + assertTrue(set.isEmpty()); + } + } +} --- /dev/null 2012-12-10 07:41:39.297440197 -0800 +++ new/test/java/util/CollectionExtensionMethods/ListExtensionMethodsTest.java 2012-12-10 21:19:11.475766251 -0800 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2012 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.concurrent.atomic.AtomicInteger; + +import org.testng.annotations.Test; + +import java.lang.reflect.Constructor; +import java.util.ConcurrentModificationException; +import java.util.function.Predicate; + +import static org.testng.Assert.*; + +/** + * @test + * @library testlibrary + * @build CollectionAsserts CollectionSupplier + * @run testng ListExtensionMethodsTest + * @summary Unit tests for extension methods on List + */ +public class ListExtensionMethodsTest { + + private static final String[] LIST_CLASSES = { + "java.util.ArrayList", + "java.util.LinkedList", + "java.util.Vector", + "java.util.concurrent.CopyOnWriteArrayList" + }; + + private static final String[] LIST_CME_CLASSES = { + "java.util.ArrayList", + "java.util.Vector" + }; + + private static final Predicate P_EVEN = x -> 0 == x % 2; + private static final Predicate P_ODD = x -> 0 != x % 2; + + private static final Comparator BIT_COUNT_COMPARATOR = + (x, y) -> Integer.bitCount(x) - Integer.bitCount(y); + + private static final Comparator ATOMIC_INTEGER_COMPARATOR = + (x, y) -> x.intValue() - y.intValue(); + + @Test + public void testUnmodifiable() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 10); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + final List unmodifiable = Collections.unmodifiableList(list); + // forEach does not modify, it should not throw UnsupportedOperationException + unmodifiable.forEach((x) -> {}); + try { + unmodifiable.removeAll(P_EVEN); + fail("removeAll on unmodifiable list did not throw exception"); + } catch (UnsupportedOperationException ignore) {} + try { + unmodifiable.replaceAll(x -> x); + fail("replaceAll on unmodifiable list did not throw exception"); + } catch (UnsupportedOperationException ignore) {} + try { + unmodifiable.sort((x, y) -> x - y); + fail("sort on unmodifiable list did not throw exception"); + } catch (UnsupportedOperationException ignore) {} + } + } + + @Test + public void testForNullPointerException() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 10); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + try { + list.forEach(null); + fail("forEach with null Block did not throw NPE"); + } catch (NullPointerException nx) {} + try { + list.removeAll((Predicate) null); + fail("removeAll with null Predicate did not throw NPE"); + } catch (NullPointerException nx) {} + try { + list.replaceAll(null); + fail("replaceAll with null UnaryOperator did not throw NPE"); + } catch (NullPointerException nx) {} + try { + list.sort(null); + fail("sort with null Comparator did not throw NPE"); + } catch (NullPointerException nx) {} + } + } + + @Test + public void testForEach() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + final List actual = new LinkedList<>(); + list.forEach(actual::add); + CollectionAsserts.assertContents(actual, list); + CollectionAsserts.assertContents(actual, original); + } + } + + @Test + public void testRemoveAll() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + list.removeAll(P_ODD); + for (final int i : list) { + assertTrue((i % 2) == 0); + } + final ListIterator li = list.listIterator(); + for (final int i : original) { + if (i % 2 == 0) { + assertEquals(li.next().intValue(), i); + } + } + assertFalse(li.hasNext()); + list.removeAll(P_EVEN); + assertTrue(list.isEmpty()); + } + } + + @Test + public void testReplaceAll() throws Exception { + final int scale = 3; + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + list.replaceAll(x -> scale * x); + for (int i=0; i < original.size(); i++) { + assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i); + } + } + } + + @Test + public void testSort() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, 1000); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + CollectionSupplier.shuffle(list); + list.sort(Integer::compare); + CollectionAsserts.assertSorted(list, Integer::compare); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + /* disabled until java.util.Comparators is availlable + CollectionSupplier.shuffle(list); + list.sort(Comparators.naturalOrder()); + CollectionAsserts.assertSorted(list, Comparators.naturalOrder()); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(Comparators.reverseOrder()); + CollectionAsserts.assertSorted(list, Comparators.reverseOrder()); + if (!test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + */ + + CollectionSupplier.shuffle(list); + list.sort(BIT_COUNT_COMPARATOR); + CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR); + // check sort by verifying that bitCount increases and never drops + int minBitCount = 0; + int bitCount; + for (final Integer i : list) { + bitCount = Integer.bitCount(i); + assertTrue(bitCount >= minBitCount); + minBitCount = bitCount; + } + + @SuppressWarnings("unchecked") + final Class> type = + (Class>) Class.forName(test.className); + final Constructor> defaultConstructor = type.getConstructor(); + final List incomparables = defaultConstructor.newInstance(); + + for (int i=0; i < test.original.size(); i++) { + incomparables.add(new AtomicInteger(i)); + } + CollectionSupplier.shuffle(incomparables); + incomparables.sort(ATOMIC_INTEGER_COMPARATOR); + for (int i=0; i < test.original.size(); i++) { + assertEquals(i, incomparables.get(i).intValue()); + } + } + } + + @Test + public void testRemoveAllThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.removeAll(list::add); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testReplaceAllThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testSortThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, 100); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.sort((x, y) -> {list.add(x); return x - y;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + +} --- /dev/null 2012-12-10 07:41:39.297440197 -0800 +++ new/test/java/util/CollectionExtensionMethods/testlibrary/CollectionAsserts.java 2012-12-10 21:19:12.207766278 -0800 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1997, 2010, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +import java.util.*; +import java.util.Iterator; +import java.util.function.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * @library + * CollectionAssert -- assertion methods for lambda test cases + */ +public class CollectionAsserts { + + public static void assertCountSum(Iterable it, int count, int sum) { + assertCountSum(it.iterator(), count, sum); + } + + public static void assertCountSum(Iterator it, int count, int sum) { + int c = 0; + int s = 0; + while (it.hasNext()) { + int i = (Integer) it.next(); + c++; + s += i; + } + + assertEquals(c, count); + assertEquals(s, sum); + } + + public static void assertConcat(Iterator it, String result) { + StringBuilder sb = new StringBuilder(); + while (it.hasNext()) { + sb.append(it.next()); + } + + assertEquals(result, sb.toString()); + } + + public static> void assertSorted(Iterator i) { + if (!i.hasNext()) + return; + T last = i.next(); + while (i.hasNext()) { + T t = i.next(); + assertTrue(last.compareTo(t) <= 0); + assertTrue(t.compareTo(last) >= 0); + last = t; + } + } + + public static void assertSorted(Iterator i, Comparator comp) { + if (!i.hasNext()) + return; + T last = i.next(); + while (i.hasNext()) { + T t = i.next(); + assertTrue(comp.compare(last, t) <= 0); + assertTrue(comp.compare(t, last) >= 0); + last = t; + } + } + + public static> void assertSorted(Iterable iter) { + assertSorted(iter.iterator()); + } + + public static void assertSorted(Iterable iter, Comparator comp) { + assertSorted(iter.iterator(), comp); + } + + public static void assertUnique(Iterable iter) { + assertUnique(iter.iterator()); + } + + public static void assertUnique(Iterator iter) { + if (!iter.hasNext()) { + return; + } + + Set uniq = new HashSet<>(); + while(iter.hasNext()) { + T each = iter.next(); + assertTrue(!uniq.contains(each)); + uniq.add(each); + } + } + + public static void assertContents(Iterable actual, Iterable expected) { + assertContents(actual.iterator(), expected.iterator()); + } + + public static void assertContents(Iterator actual, Iterator expected) { + List history = new ArrayList<>(); + + while (expected.hasNext()) { + if (!actual.hasNext()) { + List expectedData = new ArrayList<>(history); + while (expected.hasNext()) + expectedData.add(expected.next()); + fail(String.format("Premature end of data; expected=%s, found=%s", expectedData, history)); + } + T a = actual.next(); + T e = expected.next(); + history.add(a); + + if (!Objects.equals(a, e)) + fail(String.format("Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", history, e, a)); + } + if (actual.hasNext()) { + List rest = new ArrayList<>(); + while (actual.hasNext()) + rest.add(actual.next()); + fail(String.format("Unexpected data %s after %s", rest, history)); + } + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static void assertContents(Iterator actual, T... expected) { + assertContents(actual, Arrays.asList(expected).iterator()); + } + + public static boolean equalsContentsUnordered(Iterable a, Iterable b) { + Set sa = new HashSet<>(); + for (T t : a) { + sa.add(t); + } + + Set sb = new HashSet<>(); + for (T t : b) { + sb.add(t); + } + + return Objects.equals(sa, sb); + } + + public static> void assertContentsUnordered(Iterable actual, Iterable expected) { + ArrayList one = new ArrayList<>(); + for (T t : actual) + one.add(t); + ArrayList two = new ArrayList<>(); + for (T t : expected) + two.add(t); + Collections.sort(one); + Collections.sort(two); + assertContents(one, two); + } + + static void assertSplitContents(Iterable> splits, Iterable list) { + Iterator> mI = splits.iterator(); + Iterator pI = null; + Iterator lI = list.iterator(); + + while (lI.hasNext()) { + if (pI == null) + pI = mI.next().iterator(); + while (!pI.hasNext()) { + if (!mI.hasNext()) { + break; + } + else { + pI = mI.next().iterator(); + } + } + assertTrue(pI.hasNext()); + T pT = pI.next(); + T lT = lI.next(); + assertEquals(pT, lT); + } + + if (pI != null) { + assertTrue(!pI.hasNext()); + } + + while(mI.hasNext()) { + pI = mI.next().iterator(); + assertTrue(!pI.hasNext()); + } + } +} --- /dev/null 2012-12-10 07:41:39.297440197 -0800 +++ new/test/java/util/CollectionExtensionMethods/testlibrary/CollectionSupplier.java 2012-12-10 21:19:12.923766306 -0800 @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2012 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.testng.TestException; + +import static org.testng.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.Collections; +import java.util.function.Supplier; + +/** + * @library + * @summary A Supplier of test cases for Collection tests + */ +public final class CollectionSupplier implements Supplier> { + + private final String[] classNames; + private final int size; + + /** + * A Collection test case. + */ + public static final class TestCase { + + /** + * The name of the test case. + */ + public final String name; + + /** + * Class name of the instantiated Collection. + */ + public final String className; + + /** + * Unmodifiable reference collection, useful for comparisons. + */ + public final Collection original; + + /** + * A modifiable test collection. + */ + public final Collection collection; + + /** + * Create a Collection test case. + * @param name name of the test case + * @param className class name of the instantiated collection + * @param original reference collection + * @param collection the modifiable test collection + */ + public TestCase(final String name, final String className, + final Collection original, final Collection collection) { + this.name = name; + this.className = className; + this.original = List.class.isAssignableFrom(original.getClass()) ? + Collections.unmodifiableList((List) original) : + Set.class.isAssignableFrom(original.getClass()) ? + Collections.unmodifiableSet((Set) original) : + Collections.unmodifiableCollection(original); + this.collection = collection; + } + + @Override + public String toString() { + return name + " " + className + + "\n original: " + original + + "\n target: " + collection; + } + } + + /** + * Shuffle a list using a PRNG with known seed for repeatability + * @param list the list to be shuffled + */ + public static void shuffle(final List list) { + // PRNG with known seed for repeatable tests + final Random prng = new Random(13); + final int size = list.size(); + for (int i=0; i < size; i++) { + // random index in interval [i, size) + final int j = i + prng.nextInt(size - i); + // swap elements at indices i & j + final E e = list.get(i); + list.set(i, list.get(j)); + list.set(j, e); + } + } + + /** + * Create a {@code Supplier} that creates instances of specified collection + * classes of specified length. + * + * @param classNames class names that implement {@code Collection} + * @param size the desired size of each collection + */ + public CollectionSupplier(final String[] classNames, final int size) { + this.classNames = Arrays.copyOf(classNames, classNames.length); + this.size = size; + } + + @Override + public Iterable get() { + try { + return getThrows(); + } catch (Exception e) { + throw new TestException(e); + } + } + + private Iterable getThrows() throws Exception { + final Collection collections = new LinkedList<>(); + for (final String className : classNames) { + @SuppressWarnings("unchecked") + final Class> type = + (Class>) Class.forName(className); + final Constructor> + defaultConstructor = type.getConstructor(); + final Constructor> + copyConstructor = type.getConstructor(Collection.class); + + final Collection empty = defaultConstructor.newInstance(); + collections.add(new TestCase("empty", + className, + copyConstructor.newInstance(empty), + empty)); + + final Collection single = defaultConstructor.newInstance(); + single.add(42); + collections.add(new TestCase("single", + className, + copyConstructor.newInstance(single), + single)); + + final Collection regular = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + regular.add(i); + } + collections.add(new TestCase("regular", + className, + copyConstructor.newInstance(regular), + regular)); + + final Collection reverse = defaultConstructor.newInstance(); + for (int i=size; i >= 0; i--) { + reverse.add(i); + } + collections.add(new TestCase("reverse", + className, + copyConstructor.newInstance(reverse), + reverse)); + + final Collection odds = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + odds.add((i * 2) + 1); + } + collections.add(new TestCase("odds", + className, + copyConstructor.newInstance(odds), + odds)); + + final Collection evens = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + evens.add(i * 2); + } + collections.add(new TestCase("evens", + className, + copyConstructor.newInstance(evens), + evens)); + + final Collection fibonacci = defaultConstructor.newInstance(); + int prev2 = 0; + int prev1 = 1; + for (int i=0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacci.add(n); + prev2 = prev1; + prev1 = n; + } + collections.add(new TestCase("fibonacci", + className, + copyConstructor.newInstance(fibonacci), + fibonacci)); + + // variants where the size of the backing storage != reported size + // created by removing half of the elements + + final Collection emptyWithSlack = defaultConstructor.newInstance(); + emptyWithSlack.add(42); + assertTrue(emptyWithSlack.remove(42)); + collections.add(new TestCase("emptyWithSlack", + className, + copyConstructor.newInstance(emptyWithSlack), + emptyWithSlack)); + + final Collection singleWithSlack = defaultConstructor.newInstance(); + singleWithSlack.add(42); + singleWithSlack.add(43); + assertTrue(singleWithSlack.remove(43)); + collections.add(new TestCase("singleWithSlack", + className, + copyConstructor.newInstance(singleWithSlack), + singleWithSlack)); + + final Collection regularWithSlack = defaultConstructor.newInstance(); + for (int i=0; i < 2 * size; i++) { + regularWithSlack.add(i); + } + assertTrue(regularWithSlack.removeAll((x) -> x >= size)); + collections.add(new TestCase("regularWithSlack", + className, + copyConstructor.newInstance(regularWithSlack), + regularWithSlack)); + + final Collection reverseWithSlack = defaultConstructor.newInstance(); + for (int i=2 * size; i >= 0; i--) { + reverseWithSlack.add(i); + } + assertTrue(reverseWithSlack.removeAll((x) -> x < size)); + collections.add(new TestCase("reverseWithSlack", + className, + copyConstructor.newInstance(reverseWithSlack), + reverseWithSlack)); + + final Collection oddsWithSlack = defaultConstructor.newInstance(); + for (int i = 0; i < 2 * size; i++) { + oddsWithSlack.add((i * 2) + 1); + } + assertTrue(oddsWithSlack.removeAll((x) -> x >= size)); + collections.add(new TestCase("oddsWithSlack", + className, + copyConstructor.newInstance(oddsWithSlack), + oddsWithSlack)); + + final Collection evensWithSlack = defaultConstructor.newInstance(); + for (int i = 0; i < 2 * size; i++) { + evensWithSlack.add(i * 2); + } + assertTrue(evensWithSlack.removeAll((x) -> x >= size)); + collections.add(new TestCase("evensWithSlack", + className, + copyConstructor.newInstance(evensWithSlack), + evensWithSlack)); + + final Collection fibonacciWithSlack = defaultConstructor.newInstance(); + prev2 = 0; + prev1 = 1; + for (int i=0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacciWithSlack.add(n); + prev2 = prev1; + prev1 = n; + } + assertTrue(fibonacciWithSlack.removeAll((x) -> x < 20)); + collections.add(new TestCase("fibonacciWithSlack", + className, + copyConstructor.newInstance(fibonacciWithSlack), + fibonacciWithSlack)); + + } + + return collections; + } + +}