for循环中第二参数是否会多次调用分析.md

简书迁移

Posted by thrfox on June 29, 2020

背景

今面试中与面试官交流,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以内,且只会调用一次,不会每次循环中重复调用