51
String s = "";
for(i=0;i<....){
    s = some Assignment;
}

また

for(i=0;i<..){
    String s = some Assignment;
}

ループの外で「s」を使用する必要はもうありません。新しい文字列は毎回初期化されないため、最初のオプションの方がおそらく優れています。ただし、2つ目は、変数のスコープがループ自体に制限される結果になります。

編集:ミルハウズの答えに応えて。ループ内の定数に文字列を割り当てるのは無意味ではないでしょうか?いいえ、ここで「いくつかの割り当て」とは、繰り返されるリストから取得された値の変化を意味します。

また、問題は私がメモリ管理について心配しているからではありません。どちらが良いか知りたいだけです。

4

8 に答える 8

109

限られた範囲がベスト

2 番目のオプションを使用します。

for ( ... ) {
  String s = ...;
}

スコープはパフォーマンスに影響しません

(JDK のツールを使用して) それぞれからコンパイルされたコードを逆アセンブルするjavapと、どちらの場合もループがまったく同じ JVM 命令にコンパイルされることがわかります。また、 Brian R. Bondy の「オプション #3」はオプション #1 と同じであることに注意してください。より厳密なスコープを使用する場合、スタックから追加または削除されるものは何もなく、どちらの場合もスタックで同じデータが使用されます。

時期尚早の初期化を避ける

2 つのケースの唯一の違いは、最初の例では変数sが不必要に初期化されることです。これは、変数宣言の場所とは別の問題です。これにより、2 つの無駄な命令が追加されます (文字列定数をロードし、それをスタック フレーム スロットに格納するため)。優れた静的分析ツールは、 に割り当てた値を読み取っていないことを警告しs、優れた JIT コンパイラはおそらく実行時にそれを省略します。

空の宣言 (つまりString s;) を使用するだけでこれを修正できますが、これは悪い習慣と見なされ、以下で説明する別の副作用があります。

null多くの場合、変数が初期化されずに読み取られたというコンパイラ エラーを単に黙らせるために、偽の値のようなものが変数に割り当てられます。このエラーは、変数のスコープが大きすぎて、有効な値を受け取る前に宣言されていることを示唆している可能性があります。空の宣言では、すべてのコード パスを考慮する必要があります。偽の値を割り当てて、この貴重な警告を無視しないでください。

スタック スロットの節約

前述のように、JVM 命令はどちらの場合も同じですが、JVM レベルで、可能な限り限られたスコープを使用するのが最適な微妙な副作用があります。これは、メソッドの「ローカル変数テーブル」に表示されます。不必要に大きなスコープで宣言された変数を使用して、複数のループがあるとどうなるかを考えてみましょう。

void x(String[] strings, Integer[] integers) {
  String s;
  for (int i = 0; i < strings.length; ++i) {
    s = strings[0];
    ...
  }
  Integer n;
  for (int i = 0; i < integers.length; ++i) {
    n = integers[i];
    ...
  }
}

変数snは、それぞれのループ内で宣言できますが、そうではないため、コンパイラはスタック フレームで 2 つの「スロット」を使用します。それらがループ内で宣言されている場合、コンパイラは同じスロットを再利用して、スタック フレームを小さくすることができます。

本当に重要なこと

ただし、これらの問題のほとんどは重要ではありません。優れた JIT コンパイラは、無駄に代入している初期値を読み取ることができないことを認識し、代入を最適化して取り除きます。あちらこちらにスロットを保存しても、アプリケーションが成功したり壊れたりすることはありません。

重要なことは、コードを読みやすく、保守しやすいものにすることです。その点では、限定されたスコープを使用する方が明らかに優れています。変数のスコープが小さいほど、それがどのように使用され、コードへの変更がどのような影響を与えるかを理解しやすくなります。

于 2008-09-21T06:24:46.287 に答える
22

理論的には、ループ内で文字列を宣言するのはリソースの無駄です。ただし、実際には、提示した両方のスニペットは同じコード(ループ外の宣言)にコンパイルされます。

したがって、コンパイラがある程度の最適化を行っても、違いはありません。

于 2008-09-21T02:48:05.650 に答える
17

's'変数のスコープはループに限定されているため、一般的には2番目のものを選択します。利点:

  • 関数の後半で「s」が再び使用されることを心配する必要がないため、これはプログラマーにとってより良い方法です。
  • 変数のスコープが小さいため、これはコンパイラーにとってより優れており、より多くの分析と最適化を実行できる可能性があります。
  • 後で使用されないのに、なぜ's'変数がループの外で宣言されるのか不思議に思わないので、これは将来の読者にとってより良いことです。
于 2008-09-21T02:51:43.613 に答える
6

ループの速度を上げたい場合は、カウンターの横に max 変数を宣言して、条件を繰り返し検索する必要がないようにすることをお勧めします。

それ以外の

for (int i = 0; i < array.length; i++) {
  Object next = array[i];
}

私は好む

for (int i = 0, max = array.lenth; i < max; i++) {
  Object next = array[i];
}

考慮すべき他のことはすでに言及されているので、私の2セントだけです(エリクソンの投稿を参照)

グリーツ、ガハド

于 2008-09-21T13:55:10.087 に答える
4

@ Esteban Arayaの答えに少し追加するには、どちらもループを通過するたびに(some Assignment式の戻り値として)新しい文字列を作成する必要があります。これらの文字列は、いずれかの方法でガベージコレクションする必要があります。

于 2008-09-21T02:54:17.647 に答える
1

私には、問題の仕様をもっと明確にする必要があるように思えます。

s = some Assignment;

これがどのような種類の割り当てであるかについては指定されていません。割り当てが

s = "" + i + "";

次に、新しい文字列を割り当てる必要があります。

しかし、もしそうなら

s = some Constant;

s は単に定数のメモリ位置を指すだけなので、最初のバージョンの方がメモリ効率が高くなります。

解釈された言語のIMHOのforループの最適化について心配するのは少しばかげているようです。

于 2008-09-21T05:32:17.363 に答える