在JAVA中,for-each循环如何在内部工作?
当我进行函数调用时,我试图找到每个循环的工作状态。请参阅以下代码在JAVA中,for-each循环如何在内部工作?,java,Java,当我进行函数调用时,我试图找到每个循环的工作状态。请参阅以下代码 public static int [] returnArr() { int [] a=new int [] {1,2,3,4,5}; return a; } public static void main(String[] args) { //Version 1 for(int a : returnArr()) { System.out.println(a); }
public static int [] returnArr()
{
int [] a=new int [] {1,2,3,4,5};
return a;
}
public static void main(String[] args)
{
//Version 1
for(int a : returnArr())
{
System.out.println(a);
}
//Version 2
int [] myArr=returnArr();
for(int a : myArr)
{
System.out.println(a);
}
}
在版本1中,我为每个循环调用returnArr()方法,在版本2中,我显式调用returnArr()方法并将其分配给数组,然后对其进行迭代。两种场景的结果相同。我想知道哪个更有效,为什么
我认为版本2会更有效,因为我不会在每次迭代中调用方法。但令我惊讶的是,当我使用版本1调试代码时,我只看到方法调用一次
有人能解释一下它实际上是如何工作的吗当我为复杂对象编码时,哪一种效率更高/更好?显示了底层编译
让L1。。。Lm
立即成为标签序列(可能为空)
在增强型for语句之前
增强型for语句相当于基本的for
for语句
表格:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
{VariableModifier} TargetType Identifier = #a[#i];
Statement
}
T[]#a=表达式;
L1:L2:。。。Lm:
for(int#i=0;#i<#a.length;#i++){
{VariableModifier}TargetType标识符=#a[#i];
陈述
}
其中表达式
是增强for语句中:
的右侧(您的returnArr()
)。在这两种情况下,它只被计算一次:在版本1中,作为增强for语句的一部分;在版本2中,因为它的结果被分配给一个变量,然后在增强的for语句中使用。编译器只调用了一次方法returnArr()
。编译时优化:)
字节码:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=6, args_size=1
** case -1 start ***
0: invokestatic #20 // Method returnArr:()[I --> called only once.
3: dup
4: astore 4
6: arraylength
7: istore_3
8: iconst_0
9: istore_2
10: goto 28
13: aload 4 --> loop start
15: iload_2
16: iaload
17: istore_1
18: getstatic #22 // Field java/lang/System.out:Ljav
/io/PrintStream;
21: iload_1
22: invokevirtual #28 // Method java/io/PrintStream.prin
ln:(I)V
25: iinc 2, 1
28: iload_2
29: iload_3
30: if_icmplt 13
***case -2 start****
33: invokestatic #20 // Method returnArr:()[I
36: astore_1
37: aload_1
38: dup
39: astore 5
41: arraylength
42: istore 4
44: iconst_0
45: istore_3
46: goto 64
49: aload 5 --> loop start case 2
51: iload_3
52: iaload
53: istore_2
54: getstatic #22 // Field java/lang/System.out:Ljav
/io/PrintStream;
57: iload_2
58: invokevirtual #28 // Method java/io/PrintStream.prin
ln:(I)V
61: iinc 3, 1
64: iload_3
65: iload 4
67: if_icmplt 49
70: return
注意:我使用的是jdk 8。我不会像前面的一个答案那样复制粘贴,而是以可读的格式解释规范 考虑以下代码:
for (T x : expr) {
// do something with x
}
如果expr
的计算结果与您案例中的数组类型类似,则语言规范说明生成的字节码将与以下内容相同:
T[] arr = expr;
for (int i = 0; i < arr.length; i++) {
T x = arr[i];
// do something with x
}
T[]arr=expr;
对于(int i=0;i
唯一的区别是变量arr
和i
对代码或调试器不可见,很遗憾。这就是为什么对于开发来说,第二个版本可能更有用:返回值存储在调试器可以访问的变量中
在第一个版本中,
expr
只是函数调用,而在第二个版本中,您声明另一个变量并将函数调用的结果赋给该变量,然后将该变量用作expr
。我希望它们在性能上不会表现出可测量的差异,因为第二个版本中的附加变量赋值应该由JIT编译器优化掉,除非您也在其他地方使用它。foreach内部使用列表迭代器遍历列表,是的,它们之间存在差异
如果您只想遍历列表,而不想修改它,那么您应该使用foreach,或者使用列表迭代器
for (String i : myList) {
System.out.println(i);
list.remove(i); // Exception here
}
Iterator it=list.iterator();
while (it.hasNext()){
System.out.println(it.next());
it.remove(); // No Exception
}
另外,如果使用foreach传递的列表为null,那么在java.util.ArrayList.iterator()中会出现null指针异常。您自己发现,调试第一个版本时没有什么区别。因此,在代码效率方面,两个版本都没有区别?是的,我在调试时观察到了一次性调用。:)不过我还是想知道你编写代码时哪个版本更好?@Abhishek-编译器或JIT都会优化你的代码。如果你从逻辑的角度去问,那么版本2是好的。