First of all, note that there already are lots of similar questions on stackoverflow, e.g. this, this, this. However, I'd like to highlight another aspect of the problem, I don't see answered in these discussions:
Performing a benchmark of three variants of iterating over an ArrayList
(specifically ArrayList
, not other List
implementations):
Variant No. 1:
int size = list.size();
for(int i = 0; i < size; i++)
doSomething(list.get(i));
Variant No. 2:
for(Object element : list)
doSomething(element);
Variant No. 3:
list.forEach(this::doSomething);
It turns out that on my machine No. 1 and No. 3 perform equally, while No. 2 takes about 55% longer. This confirms the intuition, that the Iterator
created implicitly in No. 2 does more work during each iteration, than the traditional loop over an array (for which there are decades of compiler optimization research). Variant No. 3 of course does the same loop as No. 1 internally.
Now my actual question: Assuming that variant No. 3 is not applicable for some reason (side effects in the loop or no Java 8), what is the cleanest way of looping over a List
in Java? As mentioned in this discussion, variant No. 1 is likely much less efficient for other List
s than ArrayList
. Keeping this in mind, we could do something like this:
if(list instanceof ArrayList)
doVariant1(list);
else
doVariant2(list);
Unfortunately this rather smells like introducing boilerplate code. So, what would you recommend as a clean, as well as performant solution for iterating over a List
?
EDIT: Here is the entire code for those wanting to reproduce the results. I am using JRE 1.8.0, JavaHotspot Sever VM build 25.0-b70 on MacOS 10 with 2 GHz Intel Core i7.
private static final int ARRAY_SIZE = 10_000_000;
private static final int WARMUP_COUNT = 100;
private static final int TEST_COUNT = 100 + (int)(Math.random() + 20);
public void benchmark()
{
for(int i = 0; i < WARMUP_COUNT; i++)
test();
long totalTime = 0;
for(int i = 0; i < TEST_COUNT; i++)
totalTime += test();
System.out.println(totalTime / TEST_COUNT);
}
private long test()
{
ArrayList<Object> list = new ArrayList<>(ARRAY_SIZE);
for(int i = 0; i < ARRAY_SIZE; i++)
list.add(null);
long t = System.nanoTime();
doLoop(list);
return System.nanoTime() - t;
}
private void doLoop(ArrayList<Object> list)
{ // replace with the variant to test.
for(Object element : list)
doSomething(element);
}
private void doSomething(Object element)
{
Objects.nonNull(element);
}