| /* | |
| * 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. | |
| */ | |
| import org.testng.annotations.Test; | |
| import java.lang.reflect.Constructor; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collection; | |
| import java.util.Collections; | |
| import java.util.Iterator; | |
| import java.util.List; | |
| import java.util.ListIterator; | |
| import java.util.NoSuchElementException; | |
| import static org.testng.Assert.assertEquals; | |
| import static org.testng.Assert.assertFalse; | |
| import static org.testng.Assert.assertTrue; | |
| import static org.testng.Assert.fail; | |
| /** | |
| * @test | |
| * @run testng IteratorDefaults | |
| * @summary test extension methods on Iterator | |
| */ | |
| @Test | |
| public class IteratorDefaults { | |
| private static interface Callback { | |
| void call(List<Integer> list); | |
| } | |
| // call the callback for each recursive subList | |
| private void trimmedSubList(final List<Integer> list, final Callback callback) { | |
| int size = list.size(); | |
| if (size > 1) { | |
| // trim 1 element from both ends | |
| final List<Integer> subList = list.subList(1, size - 1); | |
| callback.call(subList); | |
| trimmedSubList(subList, callback); | |
| } | |
| } | |
| public void testRemoveUnsupported() { | |
| final Iterator iterator = new Iterator() { | |
| @Override | |
| public boolean hasNext() { | |
| return false; | |
| } | |
| @Override | |
| public Object next() { | |
| return null; | |
| } | |
| }; | |
| try { | |
| iterator.remove(); | |
| fail("expected UnsupportedOperationException from remove not thrown"); | |
| } catch (UnsupportedOperationException ignore) { | |
| } | |
| } | |
| public void testRemoveOverride() { | |
| final IteratorWithRemove iterator = new IteratorWithRemove(); | |
| iterator.remove(); | |
| assertTrue(iterator.removed); | |
| } | |
| public void testForEach() throws Exception { | |
| final Integer[] data = new Integer[1000]; | |
| for (int i=0; i < data.length; i++) { | |
| data[i] = i; | |
| } | |
| final List<Integer> source = Arrays.asList(data); | |
| final String[] iterableCollectionClasses = { | |
| "java.util.ArrayDeque", | |
| "java.util.ArrayList", | |
| "java.util.HashSet", | |
| "java.util.LinkedHashSet", | |
| "java.util.LinkedList", | |
| "java.util.PriorityQueue", | |
| "java.util.TreeSet", | |
| "java.util.Vector", | |
| "java.util.concurrent.ConcurrentLinkedDeque", | |
| "java.util.concurrent.ConcurrentLinkedQueue", | |
| "java.util.concurrent.ConcurrentSkipListSet", | |
| "java.util.concurrent.CopyOnWriteArrayList", | |
| "java.util.concurrent.CopyOnWriteArraySet", | |
| "java.util.concurrent.LinkedBlockingDeque", | |
| "java.util.concurrent.LinkedBlockingQueue", | |
| "java.util.concurrent.LinkedTransferQueue", | |
| "java.util.concurrent.PriorityBlockingQueue" | |
| }; | |
| for (final String iterableClass : iterableCollectionClasses) { | |
| final Iterable<Integer> iterable = | |
| (Iterable<Integer>) Class.forName(iterableClass).newInstance(); | |
| ((Collection<Integer>) iterable).addAll(source); | |
| final Iterator<Integer> iterator = iterable.iterator(); | |
| final List<Integer> target = new ArrayList<>(source.size()); | |
| iterator.forEachRemaining(target::add); | |
| if ("java.util.HashSet".equals(iterableClass)) { | |
| target.sort((x, y) -> x - y); | |
| assertEquals(target, source); | |
| } else { | |
| assertEquals(target, source); | |
| } | |
| // verify that for an iterator that has been advanced via next(), | |
| // forEach starts from the current location, not zero | |
| final int OFFSET = 5; | |
| final List<Integer> reference2 = new ArrayList<>(source).subList(OFFSET, source.size()); | |
| final List<Integer> removed2 = new ArrayList<>(OFFSET); | |
| final Iterator<Integer> iterator2 = iterable.iterator(); | |
| for (int i=0; i < OFFSET; i++) { | |
| // advance the iterator by OFFSET, saving iterated elements | |
| removed2.add(iterator2.next()); | |
| } | |
| final List<Integer> target2 = new ArrayList<>(reference2.size()); | |
| iterator2.forEachRemaining(target2::add); | |
| if ("java.util.HashSet".equals(iterableClass)) { | |
| assertEquals(target2.size(), reference2.size()); | |
| target2.addAll(removed2); | |
| target2.sort((x, y) -> x - y); | |
| assertEquals(target2, source); | |
| assertEquals(target2.subList(OFFSET, source.size()), reference2); | |
| } else { | |
| assertEquals(target2, reference2); | |
| } | |
| } | |
| } | |
| public void testForEachSubList() throws Exception { | |
| final Integer[] data = new Integer[100]; | |
| for (int i = 0; i < data.length; i++) { | |
| data[i] = i; | |
| } | |
| final List<Integer> source = Arrays.asList(data); | |
| final String[] listClasses = { | |
| "java.util.ArrayList", | |
| "java.util.LinkedList", | |
| "java.util.Vector", | |
| "java.util.concurrent.CopyOnWriteArrayList" | |
| }; | |
| for (final String listClass : listClasses) { | |
| final List<Integer> list = | |
| (List<Integer>) Class.forName(listClass).newInstance(); | |
| list.addAll(source); | |
| trimmedSubList(list, new Callback() { | |
| @Override | |
| public void call(final List<Integer> list) { | |
| if (list.size() < 1) { | |
| return; | |
| } | |
| final List<Integer> target = new ArrayList<>(list.size()); | |
| final ListIterator<Integer> iterator = list.listIterator(); | |
| assertTrue(iterator.hasNext()); | |
| assertFalse(iterator.hasPrevious()); | |
| assertEquals(iterator.nextIndex(), 0); | |
| assertEquals(iterator.previousIndex(), -1); | |
| iterator.forEachRemaining(target::add); | |
| assertEquals(target, list); | |
| assertFalse(iterator.hasNext()); | |
| assertTrue(iterator.hasPrevious()); | |
| assertEquals(iterator.nextIndex(), list.size()); | |
| assertEquals(iterator.previousIndex(), list.size() - 1); | |
| try { | |
| iterator.next(); | |
| fail(listClass + " iterator advanced beyond end"); | |
| } catch (NoSuchElementException ignore) { | |
| } | |
| } | |
| }); | |
| } | |
| } | |
| public void testOptimizedForEach() throws Exception { | |
| final Integer[] data = new Integer[1000 * 1000]; | |
| for (int i=0; i < data.length; i++) { | |
| data[i] = i; | |
| } | |
| final List<Integer> source = Arrays.asList(data); | |
| final String[] listClasses = { | |
| "java.util.ArrayList", | |
| "java.util.LinkedList", | |
| "java.util.Vector", | |
| "java.util.concurrent.CopyOnWriteArrayList" | |
| }; | |
| final int OFFSET = 3; | |
| final List<Integer> target = new ArrayList<>(source); | |
| for (final String listClass : listClasses) { | |
| final List<Integer> list = | |
| (List<Integer>) Class.forName(listClass).newInstance(); | |
| list.addAll(source); | |
| final ListIterator<Integer> iterator = list.listIterator(); | |
| assertFalse(iterator.hasPrevious()); | |
| for (int i=0; i < OFFSET; i++) { | |
| iterator.next(); | |
| } | |
| assertTrue(iterator.hasNext()); | |
| assertTrue(iterator.hasPrevious()); | |
| assertEquals(iterator.nextIndex(), OFFSET); | |
| assertEquals(iterator.previousIndex(), OFFSET - 1); | |
| iterator.forEachRemaining(e -> { | |
| target.set(e, e + 1); | |
| }); | |
| for (int i=OFFSET; i < data.length; i++) { | |
| assertEquals(target.get(i).intValue(), source.get(i)+1); | |
| } | |
| assertFalse(iterator.hasNext()); | |
| assertTrue(iterator.hasPrevious()); | |
| assertEquals(iterator.nextIndex(), data.length); | |
| assertEquals(iterator.previousIndex(), data.length - 1); | |
| // CopyOnWriteArrayList.listIterator().remove() is unsupported | |
| if (!"java.util.concurrent.CopyOnWriteArrayList".equals(listClass)) { | |
| for (int i = data.length - 1; i >= 0; i--) { | |
| iterator.remove(); // must not throw | |
| if (i > 0) { | |
| iterator.previous(); | |
| } | |
| } | |
| assertTrue(list.isEmpty()); | |
| } | |
| try { | |
| iterator.next(); | |
| fail(listClass + " iterator advanced beyond end"); | |
| } catch (NoSuchElementException ignore) { | |
| } | |
| } | |
| } | |
| @Test(enabled = false) | |
| public void compareForEachPerformance() throws Exception { | |
| final Integer[] data = new Integer[1000 * 100]; | |
| for (int i=0; i < data.length; i++) { | |
| data[i] = i; | |
| } | |
| final List<Integer> source = Arrays.asList(data); | |
| final String[] iterableCollectionClasses = { | |
| "java.util.ArrayList", // warmup, results discarded | |
| "java.util.ArrayDeque", | |
| "java.util.ArrayList", | |
| "java.util.HashSet", | |
| "java.util.LinkedHashSet", | |
| "java.util.LinkedList", | |
| "java.util.PriorityQueue", | |
| "java.util.TreeSet", | |
| "java.util.Vector", | |
| "java.util.concurrent.ConcurrentLinkedDeque", | |
| "java.util.concurrent.ConcurrentLinkedQueue", | |
| "java.util.concurrent.ConcurrentSkipListSet", | |
| "java.util.concurrent.CopyOnWriteArrayList", | |
| "java.util.concurrent.CopyOnWriteArraySet", | |
| "java.util.concurrent.LinkedBlockingDeque", | |
| "java.util.concurrent.LinkedBlockingQueue", | |
| "java.util.concurrent.LinkedTransferQueue", | |
| "java.util.concurrent.PriorityBlockingQueue" | |
| }; | |
| boolean warmup = true; | |
| final int ITERATIONS = 10; | |
| final Integer[] target = new Integer[source.size()]; | |
| for (final String iterableClass : iterableCollectionClasses) { | |
| final Class<? extends Collection<Integer>> type = | |
| (Class<? extends Collection<Integer>>) Class.forName(iterableClass); | |
| final Constructor<? extends Collection<Integer>> copyConstructor = | |
| type.getConstructor(Collection.class); | |
| final Iterable<Integer> iterable = copyConstructor.newInstance(source); | |
| final Iterable<Integer> reference = | |
| Collections.unmodifiableCollection((Collection<Integer>) iterable); | |
| for (int i=0; i < ITERATIONS; i++) { | |
| final Iterator<Integer> iterator = reference.iterator(); | |
| final long forEachStart = System.nanoTime(); | |
| iterator.forEachRemaining(x -> {target[x.intValue()] = x;}); | |
| final long forEachEnd = System.nanoTime(); | |
| final Iterator<Integer> iterator2 = reference.iterator(); | |
| Integer x; | |
| final long iteratorStart = System.nanoTime(); | |
| while (iterator2.hasNext()) { | |
| x = iterator2.next(); | |
| target[x.intValue()] = x; | |
| } | |
| final long iteratorEnd = System.nanoTime(); | |
| if (warmup) { continue; } // warmup, discard results | |
| final long forEachTime = forEachEnd - forEachStart; | |
| final long iteratorTime = iteratorEnd - iteratorStart; | |
| final long speedup = iteratorTime - forEachTime; | |
| System.out.print(iterableClass); | |
| System.out.print(" iterator: "); | |
| System.out.print(iteratorTime); | |
| System.out.print(", forEach: "); | |
| System.out.print(forEachTime); | |
| System.out.print(", speedup: "); | |
| System.out.print(speedup); | |
| System.out.print(" ("); | |
| System.out.print((speedup * 100) / iteratorTime); | |
| System.out.print("%)\n"); | |
| } | |
| if (warmup) { warmup = false; } | |
| System.out.println(); | |
| } | |
| } | |
| @Test(enabled = false) | |
| public void compareSubListForEachPerformance() throws Exception { | |
| final Integer[] data = new Integer[1000 * 100]; | |
| for (int i = 0; i < data.length; i++) { | |
| data[i] = i; | |
| } | |
| final List<Integer> source = Arrays.asList(data); | |
| final String[] listClasses = { | |
| "java.util.ArrayList", // warmup, results discarded | |
| "java.util.ArrayList", | |
| "java.util.LinkedList", | |
| "java.util.Vector", | |
| "java.util.concurrent.CopyOnWriteArrayList" | |
| }; | |
| boolean warmup = true; | |
| final int ITERATIONS = 10; | |
| final Integer[] target = new Integer[source.size()]; | |
| for (final String listClass : listClasses) { | |
| final Class<? extends List<Integer >> type = | |
| (Class<? extends List<Integer>>) Class.forName(listClass); | |
| final Constructor<? extends List<Integer >> copyConstructor = | |
| type.getConstructor(Collection.class); | |
| final List<Integer> iterable = copyConstructor.newInstance(source); | |
| final List<Integer> reference = Collections.unmodifiableList(iterable); | |
| for (int i = 0; i < ITERATIONS; i++) { | |
| final Iterator<Integer> iterator = reference.subList(42, reference.size() - 37).iterator(); | |
| final long forEachStart = System.nanoTime(); | |
| iterator.forEachRemaining(x -> { | |
| target[x.intValue()] = x; | |
| }); | |
| final long forEachEnd = System.nanoTime(); | |
| final Iterator<Integer> iterator2 = reference.iterator(); | |
| Integer x; | |
| final long iteratorStart = System.nanoTime(); | |
| while (iterator2.hasNext()) { | |
| x = iterator2.next(); | |
| target[x.intValue()] = x; | |
| } | |
| final long iteratorEnd = System.nanoTime(); | |
| if (warmup) { continue; } // warmup, discard results | |
| final long forEachTime = forEachEnd - forEachStart; | |
| final long iteratorTime = iteratorEnd - iteratorStart; | |
| final long speedup = iteratorTime - forEachTime; | |
| System.out.print(listClass); | |
| System.out.print(" iterator: "); | |
| System.out.print(iteratorTime); | |
| System.out.print(", forEach: "); | |
| System.out.print(forEachTime); | |
| System.out.print(", speedup: "); | |
| System.out.print(speedup); | |
| System.out.print(" ("); | |
| System.out.print((speedup * 100) / iteratorTime); | |
| System.out.print("%)\n"); | |
| } | |
| if (warmup) { warmup = false; } | |
| System.out.println(); | |
| } | |
| } | |
| static class IteratorWithRemove implements Iterator { | |
| public boolean removed; | |
| IteratorWithRemove() { | |
| removed = false; | |
| } | |
| @Override | |
| public boolean hasNext() { | |
| return false; | |
| } | |
| @Override | |
| public Object next() { | |
| return null; | |
| } | |
| @Override | |
| public void remove() { | |
| removed = true; | |
| } | |
| } | |
| } |