foreach循环是否真的被重写为使用迭代器的for循环?

7
这篇文章解释了foreach循环直接对应使用迭代器。如果我写一个foreach循环,它会被直接转换成使用迭代器的for循环吗?特别是给定以下循环:
for(Integer i : createList()){
    System.out.println(i);
}

无论如何,我是否保证只调用createList()一次?它被重写为:

for(Iterator<Integer> i = createList().iterator(); i.hasNext(); ) {
    System.out.println(i.next());
}

是在某种中间步骤中产生了相同的字节码,还是刚好产生了与上述相同的字节码?


我理解这些代码片段是等价的。有些答案说“它被翻译了”。我不明白编译器是否会直接将其与第二个片段交换,还是只是产生相同的输出。无论哪种方式,我都很好奇这种行为是由语言本身还是编译器保证的。 - Bartek Maraszek
2
@BrianRoach,您提出的重复问题不正确 - 它没有解决循环主题的多次评估问题。撤回您的投票并希望其他人使用我的重复问题进行VTC可能是一个好主意。 - Duncan Jones
@Duncan,您提到的帖子以及由user1685993提供的链接解释了我对函数调用的主要关注点。 我仍然想知道翻译在实践中是什么样子的(是否存在中间步骤或缺乏中间步骤)。 我认为,如果没有中间步骤,可能会更难保证构造始终完全等效(我的想法正确吗?)。 也许有人恰好知道这一点 :) - Bartek Maraszek
无论是否存在“真正”的翻译,或者只是等效的字节码输出,都不重要,因为Java编译器不需要输出它们的中间数据结构。只要字节码输出正确,Java编译器(而且不仅仅是JDK提供的编译器 - 比如Eclipse编译器)可以采取任何方式来到达输出结果。 - Erwin Bolwidt
2个回答

6
根据Oracle文档,确实会生成代码,但根据使用的对象类型而异。
如果您正在使用数组,则foreach循环将被翻译为带有索引的for循环:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    VariableModifiersopt TargetType Identifier = #a[#i];
    Statement
    }

如果您有一个Iterable对象,您可以像这样使用迭代器进行循环遍历:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
    VariableModifiersopt TargetType Identifier = (TargetType) #i.next();
    Statement
}

1
据我所知,它不会经过翻译阶段,它只是对可迭代对象执行相同的操作。
一个区别是你不能在 for-each 中访问迭代器,也就是说你不能这样做。
for(Integer i: createList()) {
   if (i == 5) iter.remove(); // or whatever the iterator is called.
}

但你可以做到。
for(Iterator<Integer> iter = createList().iterator(); iter.hasNext(); ) {
    Integer i = iter.next();

    if (i == 5) iter.remove();
}

注意:如果您有

public Integer[] createList();

那么

for(Integer i : createList()){
    System.out.println(i);
}

is the same as

{
    Integer[] $arr = createList();
    for(int $i = 0; $i < $arr.length; $i++) {
        Integer i = $arr[$i];
        System.out.println(i);
    }
}

注意:变量$arr$i是虚构的,不可用于应用程序中。您可能可以在调试器中看到它们,但名称可能不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接