for loop optimization
Sorry to say, but @Jigar's answer is incorrect. This is the correct answer. (tldr; don't use for : each
).
import java.util.ArrayList;
import java.util.List;
public class LoopTest {
public static void main(String s[]) {
long start, end;
List<Integer> a = new ArrayList<Integer>();
for (int i = 0; i < 2500000; i++) {
a.add(i);
}
///// TESTING FOR : EACH LOOP
start = System.currentTimeMillis();
for (Integer j : a) {
int x = j + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ Integer j : a ] ");
////// TESTING DEFAULT LOOP
start = System.currentTimeMillis();
for (int i = 0; i < a.size(); i++) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = 0; i < a.length; i++ ] ");
////// TESTING SLIGHTLY OPTIMIZED LOOP
start = System.currentTimeMillis();
int size = a.size();
for (int i = 0; i < size; i++) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = 0; i < size; i++ ] ");
//// TESTING MORE OPTIMIZED LOOP
start = System.currentTimeMillis();
for (int i = size; --i >= 0;) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = size; --i >= 0; ] ");
}
}
The results:
96 milli seconds for [ Integer j : a ]
57 milli seconds for [ int i = 0; i < a.length; i++ ]
31 milli seconds for [ int i = 0; i < size; i++ ]
31 milli seconds for [ int i = size; --i >= 0; ]
You can make up your own mind, but too much attribution is given to the JVM optimizer. You still have to be smart with your own code, and using for : each
notation is NOT a good idea (almost ever). As you can see, you have a good idea by putting size in its own variable.
Even though some of these optimization may be JVM-dependent (and some may kick in with the JIT), it's important to know what Java does and what Java doesn't do.
It is better to use for-each loop [more readable]
for (Flower flower :flowers){
//...
}
I have dumped instructions using javap
for the following code:
public void forLoop1() {
List<String> lst = new ArrayList<String>();
for (int i = 0; i < lst.size(); i++) {
System.out.println("hi");
}
}
public void forLoop2() {
List<String> lst = new ArrayList<String>();
int size = lst.size();
for (int i = 0; i < size; i++) {
System.out.println("hi");
}
}
public void forLoop1();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: aload_1
12: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I
17: if_icmpge 34
20: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #6; //String hi
25: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
28: iinc 2, 1
31: goto 10
34: return
public void forLoop2();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I
14: istore_2
15: iconst_0
16: istore_3
17: iload_3
18: iload_2
19: if_icmpge 36
22: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
25: ldc #6; //String hi
27: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
30: iinc 3, 1
33: goto 17
36: return
It doesn't optimize for me.
java version "1.6.0_22" Java(TM) SE Runtime Environment (build 1.6.0_22-b04) Java HotSpot(TM) Client VM (build 17.1-b03, mixed mode, sharing)
So if you need to choose from mentioned two, go for second, but I personally would go for for-each
.
for-each Performance
From Item 46 in Effective Java by Joshua Bloch :
The for-each loop, introduced in release 1.5, gets rid of the clutter and the opportunity for error by hiding the iterator or index variable completely. The resulting idiom applies equally to collections and arrays:
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }
When you see the colon (:), read it as “in.” Thus, the loop above reads as “for each element e in elements.” Note that there is no performance penalty for using the for-each loop, even for arrays. In fact, it may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes the limit of the array index only once. While you can do this by hand (Item 45), programmers don’t always do so.
See Also
- Is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop
The JVM can't optimize it because size()
is a method, and JVM can't (and won't try to) determine that the size()
will always return the same value in this context. Provided size()
value doesn't change, the second one is slightly more performant, but the gain is so, so slight that you don't really have to even consider using it.