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()
- If
Iterator.hasNext()
: ループを呼び出しIterator.next()
て続行する
「この無駄なループがコンパイルされたコードから最適化されないのはなぜですか?リスト項目で何もしないことがわかります」:まあ、.iterator()
副作用のある反復可能オブジェクトをコーディングすることは可能です、または.hasNext()
副作用または意味のある結果をもたらすもの。
データベースからのスクロール可能なクエリを表す iterable が何か劇的なことをする可能性があることは容易に想像できます.hasNext()
(データベースに接続したり、結果セットの最後に到達したためにカーソルを閉じたりするなど)。
したがって、ループ本体で何も起こらないことを証明できたとしても、反復時に意味のある/結果的なことが何も起こらないことを証明するのは、よりコストがかかります (扱いにくい?)。コンパイラは、この空のループ本体をプログラムに残す必要があります。
期待できる最善の方法は、コンパイラの警告です。この空のループ本体について警告javac -Xlint:all Whatever.java
しないのは興味深いことです。ただし、IntelliJ IDEA はそうします。確かに、Eclipse Compiler を使用するように IntelliJ を構成しましたが、それが理由ではないかもしれません。