序
算是解放了吧,离开了独立生活了近一年的城市,再多的不舍还是抵不过家的温暖。要是还是一年前的自己,可能此时的心态应该是放飞自我,约上几个朋友都不知道在哪里吃着宵夜了。现在更多的是亲情的陪伴。回来第一天晚上,发现自己对于熬夜这件事情是越来越执着了,最近在看些值得一看的影视作品《亮剑》、《让子弹飞》、《好先生》等等,不是说自己有多闲,只是觉得对于生活的思考还是很欠缺的。还是那句话:努力的人,自带光芒。加油!
晚上趁着空闲,捋了一下学习路线。参考一些大牛的技术博客的时候看到这样一句话:尽管基础再差,如果能够在写代码的时候,能把阿里巴巴代码规范插件中的所有警告让他们全部闭嘴的话,一定时间积累下来,水平也不会差到那里去。 寻思是否有那么神奇,那就试试呗,有效就成神,没有就算了呗,当下时间真是个不值钱的东西。
传说的神奇插件,就是这玩意(看图):

Question
嘿嘿~~~发现一个比较有趣的问题,是发现阿里巴巴的代码规范插件给出警告说,for 语句可以换成 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 中也有使用,详细看图:

这部分已经涉及知识的盲区了,但是我有个大胆的想法,既然有变量复用,那是不是意味着跟编译顺序有关系?废话不多说,直接运行几次,看结果:
- 第一次结果:
--------比较 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 中存在某种机制(某种编译原理,尚未知悉),以致出现由编译顺序的不同导致编译时间的不同,即编译时间(运行时间)是受编译顺序的不同而不同。
如有知悉,还望指教。晚安!
人若无名,专心练剑!
6557





