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()
副作用または意味のある結果をもたらします。
データベースからのスクロール可能なクエリを表すイテラブルが劇的な何かをする.hasNext()
(データベースに接続する、または結果セットの最後に到達したためにカーソルを閉じるなど)と簡単に想像できます。
したがって、ループ本体で何も起こらないことを証明できたとしても、反復しても意味のある/結果が何も起こらないことを証明する方がコストがかかります(扱いにくい?)。コンパイラーはこの空のループ本体をプログラムに残さなければなりません。
私たちが期待できる最高のものは、コンパイラの警告です。この空のループ本体について警告javac -Xlint:all Whatever.java
しないのは興味深いことです。IntelliJ IDEAはそうします。確かに、Eclipseコンパイラを使用するようにIntelliJを設定しましたが、それが理由ではないかもしれません。
![ここに画像の説明を入力してください](https://i.stack.imgur.com/pAVRT.png)