Java 中 for 和 foreach 效率的思考

算是解放了吧,离开了独立生活了近一年的城市,再多的不舍还是抵不过家的温暖。要是还是一年前的自己,可能此时的心态应该是放飞自我,约上几个朋友都不知道在哪里吃着宵夜了。现在更多的是亲情的陪伴。回来第一天晚上,发现自己对于熬夜这件事情是越来越执着了,最近在看些值得一看的影视作品《亮剑》、《让子弹飞》、《好先生》等等,不是说自己有多闲,只是觉得对于生活的思考还是很欠缺的。还是那句话:努力的人,自带光芒。加油!

晚上趁着空闲,捋了一下学习路线。参考一些大牛的技术博客的时候看到这样一句话:尽管基础再差,如果能够在写代码的时候,能把阿里巴巴代码规范插件中的所有警告让他们全部闭嘴的话,一定时间积累下来,水平也不会差到那里去。 寻思是否有那么神奇,那就试试呗,有效就成神,没有就算了呗,当下时间真是个不值钱的东西。

传说的神奇插件,就是这玩意(看图):
阿里巴巴代码规范插件

Question

嘿嘿~~~发现一个比较有趣的问题,是发现阿里巴巴的代码规范插件给出警告说,for 语句可以换成 foreach,如图:
for replace with foreach
我就纳闷了,为啥要换呢?阿里巴巴代码规范是为了干嘛,肯定是效率啊,那就先把运行时间打印出来。一顿操作猛如虎,代码如图:

public static void main(String[] args) {
     System.out.println("Hello World!");
     int[][] list = new int[3][4];
     for (int i = 0; i < list.length; i++) {
         for (int j = 0; j < list[i].length; j++) {
             list[i][j] = i * 10 + j;
         }
     }

     System.out.println("--------比较 for or foreach运行效率---------");
     System.out.println("#### for 运行结果 ---->>>>>");
     long startTime1 = System.nanoTime();
     for (int first = 0; first < list.length; first++) {
         for (int second = 0; second < list[first].length; second++) {
             System.out.print(list[first][second] + "\t");
         }
         System.out.println();
     }
     long endTime1 = System.nanoTime();
     System.out.println("程序运行时间: " + (endTime1 - startTime1) + "us");

     System.out.println("#### foreach 运行结果 ---->>>>>");
     long startTime2 = System.nanoTime();
     for (int[] i : list) {
         for (int j : i) {
             System.out.print(j + "\t");
         }
         System.out.println();
     }
     long endTime2 = System.nanoTime();
     System.out.println("程序运行时间: " + (endTime2 - startTime2) + "us");
 }

好了,看下运行结果(意料之中):

--------比较 for or foreach运行效率---------
#### for 运行结果 ---->>>>>
20	21	22	23	
10	11	12	13	
0	1	2	3	
程序运行时间: 196400us
#### foreach 运行结果 ---->>>>>
20	21	22	23	
10	11	12	13	
0	1	2	3	
程序运行时间: 139700us

结果竟然差这么多,震惊!!!

刨根问底

效率差距如此之大,难怪插件会提示替换。但是为啥呢,有位老铁说可能是:“是否小于那个条件那里,那里判断估计是占了一些时间”。既然如此,那就尝试下在 foreach 中添加“等价”的判断(随便添加个 if 语句就算了,技术水平限制只能尽可能的模拟相似)。那就来吧,直接贴代码:

public static void main(String[] args) {
    System.out.println("Hello World!");
    int[][] list = new int[3][4];
    for (int i = 0; i < list.length; i++) {
        for (int j = 0; j < list[i].length; j++) {
            list[i][j] = i * 10 + j;
        }
    }
    
    System.out.println("--------比较 for or foreach运行效率---------");
    System.out.println("#### for 运行结果 ---->>>>>");
    long startTime1 = System.nanoTime();
    for (int first = 0; first < list.length; first++) {
        for (int second = 0; second < list[first].length; second++) {
            System.out.print(list[first][second] + "\t");
        }
        System.out.println();
    }
    long endTime1 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime1 - startTime1) + "us");

    System.out.println("#### foreach 运行结果 ---->>>>>");
    long startTime2 = System.nanoTime();
    for (int[] i : list) {
        // 添加判断语句
        if (list.length > 0);
        for (int j : i) {
            System.out.print(j + "\t");
            // 添加判断语句
            if (list.length > 0);
        }
        System.out.println();
    }
    long endTime2 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime2 - startTime2) + "us");
}

运行结果:

--------比较 for or foreach运行效率---------
#### for 运行结果 ---->>>>>
20	21	22	23	
10	11	12	13	
0	1	2	3	
程序运行时间: 261000us
#### foreach 运行结果 ---->>>>>
20	21	22	23	
10	11	12	13	
0	1	2	3	
程序运行时间: 106500us

从结果看,似乎与 for 的条件判断有点关系,但是究竟是不是因为这个关系呢,我们看下编译产生的.class文件,直接贴代码:

public static void main(String[] args) {
    System.out.println("Hello World!");
    int[][] list = new int[3][4];
    int j;
    for(int i = 0; i < list.length; ++i) {
        for(j = 0; j < list[i].length; ++j) {
            list[i][j] = i * 10 + j;
        }
    }
    System.out.println("--------比较 for or foreach运行效率---------");
    System.out.println("#### for 运行结果 ---->>>>>");
    long startTime1 = System.nanoTime();
    for(first = 0; first < list.length; ++first) {
        for(int second = 0; second < list[first].length; ++second) {
            System.out.print(list[first][second] + "\t");
        }
        System.out.println();
    }
    long endTime1 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime1 - startTime1) + "us");
    System.out.println("#### foreach 运行结果 ---->>>>>");
    long startTime2 = System.nanoTime();
    int[][] var8 = list;
    int var9 = list.length;
    for(int var10 = 0; var10 < var9; ++var10) {
        int[] i = var8[var10];
        if (list.length > 0) {
        }
        int[] var12 = i;
        int var13 = i.length;
        for(int var14 = 0; var14 < var13; ++var14) {
            int j = var12[var14];
            System.out.print(j + "\t");
            if (list.length > 0) {
            }
        }
        System.out.println();
    }
    long endTime2 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime2 - startTime2) + "us");
}

其实通过.class文件可以一目了然的看出来,foreach 在编译过程中也是转换成 for 的运行的,所以上述假设并不成立。但是发现了一个问题,foreach 在编译的时候会转化成 for 而且编译生成的 for 与自己写的有点不一样,如果按照编译生成的来写,效果会怎样呢?
废话不多说,直接贴代码:

public static void main(String[] args) {
    System.out.println("Hello World!");
    int[][] list = new int[3][4];
    for (int i = 0; i < list.length; i++) {
        for (int j = 0; j < list[i].length; j++) {
            list[i][j] = i * 10 + j;
        }
    }
    System.out.println("--------比较 for or foreach运行效率---------");
    System.out.println("#### for 运行结果 ---->>>>>");
    long startTime1 = System.nanoTime();
    // 改写的代码
    int[][] var8 = list;
    int var9 = list.length;
    for(int var10 = 0; var10 < var9; ++var10) {
        int[] i = var8[var10];
        int[] var12 = i;
        int var13 = i.length;
        for(int var14 = 0; var14 < var13; ++var14) {
            int j = var12[var14];
            System.out.print(j + "\t");
        }
        System.out.println();
    }
    long endTime1 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime1 - startTime1) + "us");
    System.out.println("#### foreach 运行结果 ---->>>>>");
    long startTime2 = System.nanoTime();
    for (int[] i : list) {
        // 添加判断语句
          if (list.length > 0);
        for (int j : i) {
            System.out.print(j + "\t");
            // 添加判断语句
              if (list.length > 0);
        }
        System.out.println();
    }
    long endTime2 = System.nanoTime();
    System.out.println("程序运行时间: " + (endTime2 - startTime2) + "us");
}

运行结果:

--------比较 for or foreach运行效率---------
#### for 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 412800us
#### foreach 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 103400us

结果还是 for 耗时长,对比下.class文件,代码如下:

System.out.println("--------比较 for or foreach运行效率---------");
System.out.println("#### for 运行结果 ---->>>>>");
long startTime1 = System.nanoTime();
int[][] var8 = list;
int var9 = list.length;
int j;
for(int var10 = 0; var10 < var9; ++var10) {
    int[] i = var8[var10];
    int[] var12 = i;
    int var13 = i.length;
    for(int var14 = 0; var14 < var13; ++var14) {
        j = var12[var14];
        System.out.print(j + "\t");
    }
    System.out.println();
}

long endTime1 = System.nanoTime();
System.out.println("程序运行时间: " + (endTime1 - startTime1) + "us");
System.out.println("#### foreach 运行结果 ---->>>>>");
long startTime2 = System.nanoTime();
int[][] var21 = list;
j = list.length;
for(int var12 = 0; var12 < j; ++var12) {
    int[] i = var21[var12];
    int[] var14 = i;
    int var15 = i.length;
    for(int var16 = 0; var16 < var15; ++var16) {
        int j = var14[var16];
        System.out.print(j + "\t");
    }
    System.out.println();
}
long endTime2 = System.nanoTime();
System.out.println("程序运行时间: " + (endTime2 - startTime2) + "us");

基本无异,但是仔细看下来,会发现,其实在 for 中定义的变量 j 在 foreach 中也有使用,详细看图:
j变量
这部分已经涉及知识的盲区了,但是我有个大胆的想法,既然有变量复用,那是不是意味着跟编译顺序有关系?废话不多说,直接运行几次,看结果:

  • 第一次结果:
--------比较 for or foreach运行效率---------
#### foreach 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 918900us
#### for 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 102200us
  • 第二次结果:
--------比较 for or foreach运行效率---------
#### foreach 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 631200us
#### for 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 104500us
  • 第三次结果:
--------比较 for or foreach运行效率---------
#### foreach 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 1672700us
#### for 运行结果 ---->>>>>
0	1	2	3	
10	11	12	13	
20	21	22	23	
程序运行时间: 608900us

End(总结)

经过上述一系列的试验,结果很明显了(虽然涉及了知识的盲区,但是还是瞎总结一番

在 JVM 中存在某种机制(某种编译原理,尚未知悉),以致出现由编译顺序的不同导致编译时间的不同,即编译时间(运行时间)是受编译顺序的不同而不同。

如有知悉,还望指教。晚安!

人若无名,专心练剑!

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值