日期:2014-05-20  浏览次数:20756 次

两种写法效率一样吗
No.1

String   pro   =   null;
Bean   bean   =   null;
for(int   i   =0;i <list1.size();i++){
bean   =   (Bean)   list1.get(i);
pro   =   bean.getpro();
list2.add(pro);
}


No.2

for(int   i   =0;i <list1.size();i++){
Bean   bean   =   (Bean)   list1.get(i);
String   pro   =   bean.getPro();
list2.add(pro);
}


明显觉得第二种好,却有人和我说第一种效率高?

------解决方案--------------------
越说越跑题...
楼主讨论的问题是:在for循环外面定义变量和在循环里面定义变量的问题.
抽象一下这个问题:
第一个对应:
public void f() {
Integer num = null;
for(int i=0;i <1000;i++)
num = new Integer(i);
}
第二个对应:
public void f() {
for(int i=0;i <1000;i++)
Integer num = new Integer(i);
}
这两个效率一样嘛?
一不一样看看.class文件怎么写的:
用javap -verbose打开class文件
第一个f()对应:
public void f();
Code:
Stack=3, Locals=3, Args_size=1
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: iload_2
5: sipush 1000
8: if_icmpge 26
11: new #2; //class java/lang/Integer
14: dup
15: iload_2
16: invokespecial #3; //Method java/lang/Integer. " <init> ":(I)V
19: astore_1
20: iinc 2, 1
23: goto 4
26: return
}

第二个对应:
public void f();
Code:
Stack=3, Locals=3, Args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 24
9: new #2; //class java/lang/Integer
12: dup
13: iload_1
14: invokespecial #3; //Method java/lang/Integer. " <init> ":(I)V
17: astore_2
18: iinc 1, 1
21: goto 2
24: return
}
看看区别吧:
第一个f()多了两条指令
aconst_null //把null常量压栈
astore_1 //把这个null弹出,存入局部变量区
好了,现在来说说java中的栈.根据 <深入java虚拟机> 中的说法,每当启动一个新的线程的时候,虚拟机就会为其分配一个java栈.这个栈是以栈帧为单位的.每当执行一个方法的时候虚拟机都会在这个java栈中压入一个栈帧,每当方法结束的时候,便会弹出这个栈帧.
这个栈帧有两个主要的部分,一个是局部变量区,一个是操作数栈.
看一眼前面代码中的Stack=3, Locals=3,前者表示这个栈帧使用的最大操作栈数,后者表示最大局部变量数.
所谓的局部变量,和类的成员变量是有本质区别的.
局部变量被编译后在class文件中连名字实际上都没有.
一个局部变量的定义如:Integer i;
被编译后不会对应任何虚拟机指令.
直到他被初始化,如:i = null;
编译器才会把这个操作转换为两条指令:
就是上面看到的
aconst_null //把null常量压栈
astore_1 //把这个null弹出,存入局部变量区
所以用第一种方式编译后会比第二种方式多了这两条指令.
但是看看代码中的goto,可以知道这两天指令并不在循环中.所以是不会对效率有影响的.
另外,从上面的代码可以看到:
for(int i=0;i <1000;i++)
Integer num = new Integer(i);
这种方式并不是每次循环都使用一个新的局部变量,而是一直在重用一个局部变量区的位置.
这个例子中用于存放num的始终都是局部变量区 "2 "这个位置.编译器已经把它优化了.
要说编译速度可能会不一样,但是虚拟机执行的效率是一样的,虚拟机能看到的这两段代码是一样的.(除了那个null)


再说说这个:
for(int i =0;i <list1.size();i++)
list2.add(((Bean)list1.get(i)).getPro());

for(int i =0;i <list1.size();i++){
Bean bean = (Bean) list1.get(i);
String pro = bean.getPro();
list2.add(pro);
}
前者和后者的效率就不一样了.前者要比后者少两个局部变量,也就是说最大局部变量数会少2.
比如:Bean bean = (Bean) list1.get(i);
首先是(Bean) list1.get(i)的结果压入栈,然后再将这个结果弹出并存入局部变量区(bean对应的位置)
当执行String pro = bean.getPro();时,还要将刚存入局部变量区的内容取出来,压入栈,进行操作,然后再将结果压入栈,再弹出并存入局部变量区(pro对应的位置)
然后是list2.add(pro),这里还要把刚才存入局部变量区的内容弹出来,在进行操作.
但如果是这样写:
list2.add(((Bean)list1.get(i)).getPro());
就会少两次栈和局部变量之间的操作.
不幸的是这两次操作是在循环内部的,所以会对效率有影响.
有耐心的话可以用javap -verbose都打开比比看,一般都会少成对出现的:
astore_n
aload_n
指令

下面是 <深入java虚拟机> 上对java栈中操作的一个演示,希望对理解有所帮助.
http://www.artima.com/insidejvm/applets/EternalMath.html