I have this loop
for (it= someCollection.iterator; it.hasNext(); )
{
//some code here
}
I changed it to:
for (it= someCollection.iterator;; )
{
if (!it.hasNext())
break;
//some code here
}
The second code ran a little bit faster in unit tests in junit on eclipse.
Is the second loop faster? I’m asking because the times given by Junit are not too exact, but they give an approximate value
When looking into this sort of problems, it’s useful to think about the generated bytecode in terms of block control flow graph, where a block is a sequence of bytecode instructions that can only be entered from its first instruction and only left after its last instruction (leaving out exits to simplify the problem).
Using this example code:
You would get the following block control flow graph. I’ve put back the equivalent bytecode into source for readability, but all the instructions generated by
System.out.println(it.next());belong to one block, since you can’t jump in the middle or get out of it.If you check a compiler book, you’ll find that
it.hasNext()dominatesSystem.out.println(it.next())because you need to go throughit.hasNext()to go toSystem.out.println(it.next()). The edge fromSystem.out.println(it.next())toit.hasNext()is called a back-edge because it links a node to one of its dominators. This is what formally defines what the loop is. The first statement in thefor-loop (Iterator it = c.iterator()) doesn’t actually belong to the loop. There is no difference with a while loop preceded by this statement, except for the scope of the declared variable, but this doesn’t matter once compiled.The first block (
it.hasNext()) is the loop header.A second example like this would produce the same graph:
The main difference is that there may be some useless
gotostatements depending on the compiler strategy.If you look at the generated bytecode using
javap -cfor these two examples, you get this (this was compiled withjavac, you may get something slightly different if you compile with the Eclipse compiler, for example):The only difference is that the first one uses
ifeq 37to go straight to the end or proceed with the next block (22), whereas the other one usesifneto go to the block aftergoto(25, equivalent to 22 in the other) and uses agototo go to the end otherwise. This is effectively equivalent and a modern JIT compiler should optimise this little difference without trouble. Apart from this, your two loops are exactly the same.I’m not sure how you’ve made your measurements, but you should also be aware that it’s not because
System.nanoTime()give you a result in nanoseconds that it has a resolution of that order, far from that. High-resolution timers are quite hard to implement and will depend on the hardware and OS. See JavaDoc:It’s likely that, if you don’t get a high enough difference, you won’t get something significant compared to the resolution.