blob: 7f6a23206dfd0ca4053a681ed50378c843aa9c45 [file] [log] [blame]
/*
* 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;
}
}
}