You can compile the methods and compare their bytecode to see the differences.
The biggest difference is between func1
and func2
where func1
only calls size()
once, whereas func2
re-calls size()
for every iteration. If size()
is not cached (most implementations do cache it) then it could be costly to re-compute it. Even in that case the jitter can probably optimize it, especially with a local variable, since it might realize that the list does not change. But if the list were an exposed member then it might need to re-compute the size each time since a separate thread could have modified the list, though it's still very likely to be optimized to assume that the size has not changed.
The only difference between func3
and fun4
is that func4
uses an extra astore
and aload
since it's storing the list in a local variable, which can probably be optimized away by the jitter.
Ultimately the best way to test efficiency is to measure it.
private void func1() {
ArrayList<String> list = new ArrayList<>();
int x = list.size();
for (int i = 0; i < x; i++)
System.out.println(list.get(i));
}
private void func2() {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < list.size(); i++)
System.out.println(list.get(i));
}
private void func3() {
for (String str : new ArrayList<String>())
System.out.println(str);
}
private void func4() {
ArrayList<String> list = new ArrayList<>();
for (String str : list)
System.out.println(str);
}
private void func1();
... storing the list ...
8 aload_1 [list]
9 invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
12 istore_2 [x] // store list.size()
13 iconst_0
14 istore_3 [i]
15 goto 35
... print ...
32 iinc 3 1 [i] // i++
35 iload_3 [i] // load i
36 iload_2 [x] // load the already computed list.size()
37 if_icmplt 18 // if i < list.size() goto 18
40 return
private void func2();
... storing the list ...
8 iconst_0
9 istore_2 [i]
10 goto 30
... print ...
27 iinc 2 1 [i] // i++
30 iload_2 [i] // load i
31 aload_1 [list] // load the list
32 invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
35 if_icmplt 13 // if i < list.size() goto 13
38 return
private void func3();
... storing the list ...
7 invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
10 astore_2
11 goto 31
14 aload_2
15 invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
... print ...
31 aload_2
32 invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
37 ifne 14
40 return
private void func4();
... storing the list ...
7 astore_1 [list] // these are the only extra operations
8 aload_1 [list] // to store the list in a local variable
9 invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
12 astore_3
13 goto 33
16 aload_3
17 invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
... print ...
33 aload_3
34 invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
39 ifne 16
42 return