Javaでは、昔ながらの方法で配列を反復処理するのが高速ですか、
for (int i = 0; i < a.length; i++)
f(a[i]);
または、より簡潔な形式を使用して、
for (Foo foo : a)
f(foo);
ArrayListの場合、答えは同じですか?
もちろん、膨大な量のアプリケーションコードについては、識別可能な違いはないので、答えは読みやすいように、より簡潔な形式を使用する必要があります。しかし、私が検討しているコンテキストは、何十億回も実行する必要がある操作を伴うヘビーデューティな技術計算です。
配列をループしている場合、それは重要ではありません-拡張forループはとにかく配列アクセスを使用します。
たとえば、次のコードを検討してください。
public static void main(String[] args)
{
for (String x : args)
{
System.out.println(x);
}
}
javap -c Test
で逆コンパイルすると、次のようになります(main
メソッドの場合):
public static void main(Java.lang.String[]);
Code:
0: aload_0
1: astore_1
2: aload_1
3: arraylength
4: istore_2
5: iconst_0
6: istore_3
7: iload_3
8: iload_2
9: if_icmpge 31
12: aload_1
13: iload_3
14: aaload
15: astore 4
17: getstatic #2; //Field Java/lang/System.out:Ljava/io/PrintStream;
20: aload 4
22: invokevirtual #3; //Method Java/io/PrintStream.println:(Ljava/lang/String;)V
25: iinc 3, 1
28: goto 7
31: return
次に、明示的な配列アクセスを使用するように変更します。
public static void main(String[] args)
{
for (int i = 0; i < args.length; i++)
{
System.out.println(args[i]);
}
}
これは逆コンパイルします:
public static void main(Java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: aload_0
4: arraylength
5: if_icmpge 23
8: getstatic #2; //Field Java/lang/System.out:Ljava/io/PrintStream;
11: aload_0
12: iload_1
13: aaload
14: invokevirtual #3; //Method Java/io/PrintStream.println:(Ljava/lang/String;)V
17: iinc 1, 1
20: goto 2
23: return
拡張forループにはもう少しセットアップコードがありますが、基本的には同じことをしています。イテレーターは関係しません。さらに、JITtedがさらに類似したコードを取得することを期待しています。
提案:本当に大きな違いを生む可能性があると思う場合(ループの本体が絶対に極小の場合にのみeverを行う)、実際のアプリケーションでベンチマークを行う必要があります。それが重要な唯一の状況です。
これは micro-optimization の分野に正真正銘で当てはまります。本当に関係ありません。文体的には、他の何かのためにループカウンターが必要でない限り、2番目の方が簡潔であるため、常に2番目の方が好きです。そして、それはこの種のマイクロ最適化よりもはるかに重要:可読性です。
そうは言っても、ArrayListの場合はそれほど違いはありませんが、LinkedListの方が2番目の方がはるかに効率的です。
それを測定します。すべてのパフォーマンスの質問に対する答えは、VMバージョン、プロセッサ、メモリ速度、キャッシュなどに依存する可能性があります。そのため、特定のプラットフォームで測定する必要があります。
個人的には、意図がより明確であるため、2番目のバリアントを好むでしょう。パフォーマンスが問題になる場合は、とにかく後で最適化できます-そのコードがアプリケーション全体のパフォーマンスにとって本当に重要な場合。
LinkedListの場合:
for(ClassOfElement element : listOfElements) {
System.out.println(element.getValue());
}
以前に回答されました:
配列またはRandomAccessコレクションでは、次の操作を行うことで速度をわずかに向上させることができます。
List<Object> list = new ArrayList<Object>();
for (int i=0, d=list.size(); i<d; i++) {
something(list.get(i));
}
しかし、私は一般的に心配しません。このような最適化は、コードに0.1%以上の違いをもたらすことはありません。コードが実際にどこで時間を費やしているのかを確認するには、Java with -profを呼び出してみてください。
さらに速いのは、fork-joinフレームワークのParallelArrayを使用することです(十分なデータセットがある場合)。