在增强的for循环的引擎下发生了什么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在增强的for循环的引擎下发生了什么?相关的知识,希望对你有一定的参考价值。

在探索Java的增强型for循环时,我编写了以下测试:

class test
{
    int number = 0;

    public static void main(String args[]) {
        new test();
    }

    public test() {

        int[] numbers = getNumbers();

        for(int number : numbers) {
            System.out.println("number    : " + number);
            System.out.println("numbers[0]: " + numbers[0]);

            numbers = getNumbers();
        }
    }

    public int[] getNumbers() {

        number++;

        int[] numbers = new int[5];

        for(int i = 0; i < numbers.length; i++)
            numbers[i] = number;

        return numbers;
    }
}

我很惊讶地发现我的测试结果如下:

number    : 1
numbers[0]: 1
number    : 1
numbers[0]: 2
number    : 1
numbers[0]: 3
number    : 1
numbers[0]: 4
number    : 1
numbers[0]: 5

然后看起来在首次执行循环时创建了单独的numbers实例,并使其成为不可变的。从那里,对numbers所做的任何更改只会在循环条件之外的版本中进行。

我已经通过用以下代码替换循环来确认行为至少与此类似:

for(int number : numbers) {
    System.out.println("number    : " + number);
    numbers = null;
}

这给了:

number    : 1
number    : 1
number    : 1
number    : 1
number    : 1

我的问题是:我们在这里隐藏了什么行为? numbers的第二个版本被复制了吗?它实际上是不可改变的,还是我没有用力戳它?换一种说法:

在增强的for循环的引擎下发生了什么?

答案

增强的for循环由JLS, Section 14.14.2覆盖:

  • 对于Iterable对象:

增强的for语句相当于以下形式的基本for语句:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    {VariableModifier} TargetType Identifier =
        (TargetType) #i.next();
    Statement
}
  • 对于数组:

增强的for语句相当于以下形式的基本for语句:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    {VariableModifier} TargetType Identifier = #a[#i];
    Statement
}

它创建了一个对数组的新引用,与原始引用分开。您可以根据需要修改对数组的引用,但它仍将继续迭代此对原始数组的隐式引用。

Iterables上,只保留对Iterator的引用,即使你在循环期间重新分配原始变量,它仍然会返回原始的Iterable对象。

无论是在数组还是for上使用增强的Iterable循环,都可以重新分配用作目标的引用,而不会影响迭代的结果 - 您仍将迭代目标引用引用的原始对象。

另一答案

在增强的for循环中,如下所示:

    for(int number : numbers) {

:之后的任何表达式仅被评估一次:当for循环开始时。即使在循环运行时为numbers变量设置了不同的值,Java也已经缓存了numbers的原始值并且正在迭代该值。如果在循环之后打印numbers中的值,您将看到numbers确实发生了变化,但for循环迭代的实际数组却没有。

...
public test() {

    int[] numbers = getNumbers();

    System.out.println("Array before loop starts:");
    System.out.println(Arrays.toString(numbers) + "
");

    for (int number : numbers) {
        System.out.println("number    : " + number);
        System.out.println("numbers[0]: " + numbers[0]);

        numbers = getNumbers();
    }

    System.out.println("
Array after loop finishes:");
    System.out.println(Arrays.toString(numbers));

}
...

以上打印:

Array before loop starts:
[1, 1, 1, 1, 1]

number    : 1
numbers[0]: 1
number    : 1
numbers[0]: 2
number    : 1
numbers[0]: 3
number    : 1
numbers[0]: 4
number    : 1
numbers[0]: 5

Array after loop finishes:
[6, 6, 6, 6, 6]
另一答案

引用JLS

增强的for语句相当于以下形式的基本for语句:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    {VariableModifier} TargetType Identifier = #a[#i];
    Statement
}

观察到Expression仅被评估一次。

另一答案

来自java文档

“例如,这段代码:

List<? extends Integer> l = ...
for (float i : l) ...

将被翻译为:

for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) {
    float #i0 = (Integer)#i.next();
    ...

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2

所以是的,你的观察是正确的!

另一答案

然后看来是创建了一个单独的数字实例......

嗯,是。您通过运行new int[5] 6次创建了五个新实例。并且for循环继续使用第一个。第一个没有收集垃圾,直到for循环结束,即使你不能再引用它。

不建议在循环时更改集合。

以上是关于在增强的for循环的引擎下发生了什么?的主要内容,如果未能解决你的问题,请参考以下文章

增强的 For 循环异常 [重复]

java中使用增强型的for循环有啥好处?

Java 增强 for 循环

Java 增强for循环

Java的for循环、增强for循环、lambda表达式中的forEach 三种方式优缺点比较?

foreach的真面目