背景
今面试中与面试官交流,for循环中的第二个参数i < l.size()定义为一个变量length,参数中使用i < length与前者是否与性能差别,对此我持保留态度。 下根据实际代码与汇编码进行比对。
示例代码
public static void main(String[] args) {
List<Integer> list = Stream.iterate(0, i -> i + 1).limit(256000).collect(Collectors.toList());
int length = list.size();
long start = System.nanoTime();
for (int j = 0; j < length /*list.size()*/; j++) {
list.get(j);
}
long end = System.nanoTime();
long nanoInterval = end - start;
System.out.println(nanoInterval);
}
length 5次执行时间分别为
1
2
3
4
5
6
// 单位ns 1ms=1000000ns
4445513
4838455
4408723
3520131
3467756
list.size() 5次执行时间分别为
1
2
3
4
5
6
// 单位ns 1ms=1000000ns
5230887
5201761
5078871
5830775
5653721
数据结论:会有轻微影响,差距在1ms左右
汇编码
用javap -verbose 查看实际运行的汇编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// length
44: istore 5
46: iload 5
48: iload_2
49: if_icmpge 67
52: aload_1
53: iload 5
55: invokeinterface #13, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
60: pop
61: iinc 5, 1
64: goto 46
67: invokestatic #12 // Method java/lang/System.nanoTime:()J
70: lstore 5
72: lload 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// list.size()
...
44: istore 5
46: iload 5
48: aload_1
49: invokeinterface #11, 1 // InterfaceMethod java/util/List.size:()I
54: if_icmpge 72
57: aload_1
58: iload 5
60: invokeinterface #13, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
65: pop
66: iinc 5, 1
69: goto 46
72: invokestatic #12 // Method java/lang/System.nanoTime:()J
75: lstore 5
注意在45-55行,后者多了一个调用接口的方法invokeinterface,只执行了一次
结论
将for循环中的第二参数提取出来会影响性能,但差距在1ms以内,且只会调用一次,不会每次循环中重复调用