Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在JAVA中,for-each循环如何在内部工作?_Java - Fatal编程技术网

在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是好的。