Javaで配列を反復処理する最速の方法:ループ変数と拡張forステートメント[複製]

StackOverflow https://stackoverflow.com/questions/1006395

  •  06-07-2019
  •  | 
  •  

質問

    

この質問にはすでに回答があります:

         

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 になります)、実際のアプリケーションでベンチマークする必要があります。それが唯一の重要な状況です。

他のヒント

これは、マイクロ最適化の分野に直接該当します。本当に関係ありません。文体的には、2番目の方が好きです。なぜなら、他の何かのためにループカウンターが必要でない限り、2番目の方が簡潔だからです。そして、それはこの種のマイクロ最適化よりもはるかに重要です::読みやすさ。

とはいえ、ArrayListの場合はそれほど違いはありませんが、LinkedListの方が2番目の方がはるかに効率的です。

測定します。すべてのパフォーマンスの質問に対する答えは、VMバージョン、プロセッサ、メモリ速度、キャッシュなどに依存する可能性があります。そのため、特定のプラットフォームで測定する必要があります。

個人的には、意図がより明確であるため、2番目のバリアントを好むでしょう。パフォーマンスが問題になる場合は、とにかく後で最適化できます-そのコードがアプリケーション全体のパフォーマンスにとって本当に重要な場合。

LinkedListの場合:

for(ClassOfElement element : listOfElements) {
  System.out.println(element.getValue());
}

以前に回答されました:

はforループとfor-eachループのパフォーマンスに違いはありますか?

配列またはRandomAccessコレクションでは、次の操作を行うことで速度をわずかに向上させることができます。

List<Object> list = new ArrayList<Object>();

for (int i=0, d=list.size(); i<d; i++) {
    something(list.get(i));
}

しかし、一般的には心配しません。このような最適化は、コードに0.1%以上の違いをもたらすことはありません。 -prof でjavaを呼び出して、コードが実際にどこで時間を費やしているのかを確認してください。

さらに速いのは、フォーク結合フレームワークのParallelArrayを使用することです(十分なデータセットがある場合)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top