foreach
无论如何在后台使用迭代器。它实际上只是语法糖。
考虑以下程序:
import java.util.List;
import java.util.ArrayList;
public class Whatever {
private final List<Integer> list = new ArrayList<>();
public void main() {
for(Integer i : list) {
}
}
}
让我们编译它javac Whatever.java
,
而读取的字节码拆卸main()
,使用javap -c Whatever
:
public void main();
Code:
0: aload_0
1: getfield #4 // Field list:Ljava/util/List;
4: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
9: astore_1
10: aload_1
11: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
16: ifeq 32
19: aload_1
20: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
25: checkcast #8 // class java/lang/Integer
28: astore_2
29: goto 10
32: return
我们可以看到它可以foreach
编译成以下程序:
- 使用创建迭代器
List.iterator()
- 如果
Iterator.hasNext()
:调用Iterator.next()
并继续循环
至于“为什么这个无用的循环为什么没有从编译后的代码中得到优化呢?我们可以看到它对列表项没有任何作用”:嗯,您有可能对可迭代的代码进行编码,从而.iterator()
产生副作用,否则.hasNext()
会有副作用或有意义的后果。
您很容易想到,一个表示数据库中可滚动查询的可迭代对象可能会产生重大变化.hasNext()
(例如,与数据库联系,或者因为到达结果集的末尾而关闭游标)。
因此,即使我们可以证明循环主体中什么也没有发生……要证明迭代时什么也没有有意义/结果没有发生,这是更昂贵的(难处理的吗?)。编译器必须将此空循环体留在程序中。
我们所希望的最好的结果是编译器警告。有趣的是javac -Xlint:all Whatever.java
,没有警告我们有关此空循环的主体。IntelliJ IDEA可以。诚然,我已经将IntelliJ配置为使用Eclipse Compiler,但这可能不是原因。