1. ホーム

[解決済み】for-eachループとイテレータはどちらが効率的か?

2022-04-10 02:47:15

質問

コレクションをトラバースする最も効率的な方法はどれですか?

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a) {
  integer.toString();
}

または

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();) {
   Integer integer = (Integer) iterator.next();
   integer.toString();
}

の完全な複製ではないことに注意してください。 この , これ , これ または この 最後の質問の答えの1つがそれに近いですが。これがダブらない理由は、これらのほとんどは、ループを比較するために get(i) イテレータを使うのではなく、ループの中で使うのです。

で提案したように メタ この質問に対する私の回答を掲載します。

解決方法は?

もし、すべての値を読み取るためにコレクションをさまようだけなら、イテレータを使っても新しいループ構文を使っても違いはありません。

しかし、あなたがループを意味する場合、古い "c-style" ループを意味します。

for(int i=0; i<list.size(); i++) {
   Object o = list.get(i);
}

そうすると、新しいforループ、つまりイテレータは、基礎となるデータ構造によっては、もっと効率的なものになる可能性があるのです。というのも、あるデータ構造では get(i) はO(n)演算であるため、ループはO(n)演算となる。 2 )操作になります。従来のリンクリストは、このようなデータ構造の一例である。すべてのイテレータは、基本的な要件として next() はO(1)演算であるべきで、ループはO(n)になる。

新しい for ループ構文によってイテレータが水面下で使用されていることを確認するために、次の 2 つの Java スニペットから生成されたバイトコードを比較してみましょう。まずforループ。

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a)
{
  integer.toString();
}
// Byte code
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 3
 GOTO L2
L3
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 2 
 ALOAD 2
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L2
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L3

そして2つ目は、イテレータです。

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();)
{
  Integer integer = (Integer) iterator.next();
  integer.toString();
}
// Bytecode:
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 2
 GOTO L7
L8
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 3
 ALOAD 3
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L7
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L8

ご覧の通り、生成されるバイトコードは事実上同じものなので、どちらの形式を使っても性能上のペナルティはありません。ほとんどの人はfor-eachループを選ぶと思います。