2009-07-30 15 views
42

Array.lengthと呼び出しのコストは何ですか、私は、これらの「パターン」の多くに出くわした:は、我々のアプリケーション内のfor-eachループのループに更新中

for (int i = 0, n = a.length; i < n; i++) { 
    ... 
} 

代わりの

for (int i = 0; i < a.length; i++) { 
    ... 
} 

()メソッドを各ループで呼び出す必要がないので、コレクションのパフォーマンスが向上することがわかります。しかし、配列で?

質問がありました:array.lengthは通常の変数よりも高価ですか?

+4

をデクリメント読みやすさのために - 第2のパターンまたはfor..eachループを使用してください。お願いします! –

答えて

60

いいえ、array.lengthへの呼び出しは、O(1)または一定時間の操作です。

.lengthは、publicfinalのメンバーがarrayであるため、ローカル変数よりもアクセスが遅くなりません。 (size()のようなメソッドへの呼び出しとはまったく異なります)

現代のコンパイラは、とにかく.lengthの呼び出しを最適化する可能性があります。

+7

@jjnguy: 'array.length'への呼び出しをO(1)にすることはできず、' int len = array.length;私は

+1

そんなことを心配しているのであれば、CまたはC++ imhoでプログラミングする必要があります。 (しかし、あなたが正しいかもしれない) – jjnguy

+8

@ jjnguy:ちょうどペタニックであり、すべてのO(1)が等しく作られていないことを指摘しています。 –

3

変数に変数を格納するのが少し速いかもしれません。しかし、プロファイラが問題として指摘すれば、私は非常に驚くでしょう。

バイトコードレベルでは、配列の長さの取得はarraylengthバイトコードで行われます。私はそれがiloadバイトコードよりも遅いかどうかはわかりませんが、注意すべき違いはありません。

11

私はそこに大きな違いがあるとは思っていません。たとえあったとしても、コンパイル時に最適化されていると思います。あなたは、そのようなものをマイクロ最適化しようとすると、あなたの時間を無駄にしています。最初にコードを読み取り、正しいものにしてから、プロファイラを使用し、適切なデータ構造/アルゴリズムを選択することを心配してください。次には、プロファイラのハイライト部分の最適化について心配しています。

+0

+1。あなたがループの中で信じられないほど些細なことをしていない限り、あなたが*ループ内で行っていることはおそらく '.length'に大きさの桁でアクセスするよりも時間がかかるでしょう。 –

7

配列の長さは、配列のメンバ変数(要素と同じではない)としてJavaに格納されるため、その長さをフェッチすることは、通常のクラスからメンバ変数を読み込むのと同じ。 CやC++のような古い言語の多くは長さを配列の一部として保存しないので、ループが始まる前に長さを保存したいと思うでしょう。あなたはJavaでこれを行う必要はありません。

+4

メンバー変数とまったく同じではありません。実際にはそれ自身の特別なバイトコードを持っています。

2

この回答はC#の観点からのものですが、私は同じことがJavaに当てはまると思います。 C#の

、イディオム

for (int i = 0; i < a.length; i++) { ...} 

は、配列の繰り返し処理として認識されているので、ループ内の代わりに、各配列アクセスのアレイにアクセスするときに境界チェックが回避されます。私にはわからないジッタ対コンパイラによって行われるどのくらいこの最適化の

for (int i = 0, n = a.length; i < n; i++) { ...} 

または

n = a.length; 

for (int i = 0; i < n; i++) { ...} 

これは、またはのようなコードを認識してもしなくてもよいです特に、それがJITterによって実行されている場合、私はすべての3が同じネイティブコードを生成することを期待しています。

しかし、最初の形式は、おそらく人によって読みやすくなる可能性があるので、私はそのことに言います。

+0

はい、私はJavaが同じ最適化を行うと思います(少なくとも最近のJVMはすべきです)。 –

14

私は昼食を超える時間の少し持っていた:あなたがテストを逆にした場合より高速であるとして、その後n = a.lengthショー、

  1. public static void main(String[] args) { 
        final int[] a = new int[250000000]; 
        long t; 
    
        for (int j = 0; j < 10; j++) { 
         t = System.currentTimeMillis(); 
         for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; } 
         System.out.println("n = a.length: " + (System.currentTimeMillis() - t)); 
    
         t = System.currentTimeMillis(); 
         for (int i = 0; i < a.length; i++) { int x = a[i]; } 
         System.out.println("i < a.length: " + (System.currentTimeMillis() - t)); 
        } 
    } 
    

    結果:

    n = a.length: 672 
    i < a.length: 516 
    n = a.length: 640 
    i < a.length: 516 
    n = a.length: 656 
    i < a.length: 516 
    n = a.length: 656 
    i < a.length: 516 
    n = a.length: 640 
    i < a.length: 532 
    n = a.length: 640 
    i < a.length: 531 
    n = a.length: 641 
    i < a.length: 516 
    n = a.length: 656 
    i < a.length: 531 
    n = a.length: 656 
    i < a.length: 516 
    n = a.length: 656 
    i < a.length: 516 
    

    ノートおそらくガベージコレクション(?)のために、i < a.lengthより約半分です。

  2. 250000000OutOfMemoryError270000000に持っていたので、私はずっと大きくできませんでした。

他の人が作っているのは、Javaをメモリから使い果たしても、2つの選択肢の間に大きな違いは見られません。実際に重要なことにあなたの開発時間を費やしてください。

+0

ヒープを大きくするためにjava -Xmx512Mを試してください。 – wbkang

+0

これはC/C++で重要です。 –

+0

@wbkang:私のデフォルトのヒープサイズが非常に大きな配列では不十分であることを特定することは、実際には課題のポイントではありませんでした。 –

1

Array.lengthは定数であり、JITコンパイラは両方のインスタンスでそれを見る必要があります。結果のマシンコードはどちらの場合も同じであると私は期待しています。少なくともサーバーコンパイラ用。その場合

4

、なぜあなたは逆ループを作らない:

for (int i = a.length - 1; i >= 0; --i) { 
    ... 
} 

2マイクロ最適化がここにあります

  • ループ逆転
  • はPostfixが
+0

「ループの逆転」はなぜ優れていますか? 0との比較が速いためですか? – Stroboskop

+0

はい、そうだと思います。誰かがこれのためのベンチマークを作った:http://www.mkyong.com/java/reverse-loop-versus-forward-loop-in-performance-java/ – instcode

+1

そして「なぜあなたはいないのか?コードをシンプルにしたい。私がこの質問をした理由は、質問に記載されているパターンを使用する正気な理由があるかどうかを確認するだけでした。 – Stroboskop

関連する問題