| /* |
| * 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; |
| } |
| } |
| } |