3

この質問の補足として、Javaループの効率(「for」と「foreach」)

簡単な質問があります。拡張されたforループのメモリフットプリントは大きくなりますか、それとも両方とも同じ構造にコンパイルされ、メモリフットプリントが同一になりfor(Object o : collection) { ... }、読み取り専用操作に常に適したものになりますか?

私が尋ねる理由は、私が取り組んでいるシミュレーターでは、すべてのフレーム(60 /秒)で、描画する面、レイトレーシングなど、配列内の数千のアイテムに対して操作を実行しているためです。ループ

for(Object o : collection) {
   o.doSomething(); 
}

コレクションのメモリコピーを作成する可能性があるようです(以前の読み取りから作成したことを思い出しているようです)。これはほとんどの状況で問題ありませんが、30000レイトレースを1秒間に60回実行した場合は問題ありません。

一方、ループは明らかです

count = collection.size();
for(i = 0; i < count; i++) {
   collection[i].doSomething();
}

参照によってすべてを実行し、読みにくいにもかかわらず、フットプリントが比較的小さい(正直なところ、それほど多くはありませんが)

何かアイデアはありますか?

(注:forループの読み取りの難しさの影響は、複数のレイヤーがある場合にのみ明らかです-単一レイヤーのforの場合、ゲインは非常に小さいです。これは経験から言うと...collection[i].property.subcollection[j].row[k].col[l].prop.subtable[m]特に一部の場合は気が遠くなるでしょうそれらのうち、キャストする必要があるもの:((Type3)((Type2)((Type1)collection[i]).property.subcollection[j].row[k]).col[l].prop).subtable[m]たとえば。)

4

2 に答える 2

2

hteを使用するArrayListがある場合、次のパターンはマイクロ最適化になります

for(int i=0, len=list.size(); i < len; i++)

for-eachループは常にイテレータを作成するため、保存するメモリは約16〜24バイトです。

(60 /秒)描画する面、レイトレーシングする面など、配列内の数千のアイテムに対して操作を実行しています。

1秒あたり60,000の場合、各アイテムで比較的重要な作業を行っているため、特に違いに気付くことはほとんどありません。

コレクションのメモリコピーを作成する可能性があるようです

リストをループしているときにリストを変更すると、ConcurrentModicationExceptionが発生する可能性があるのはそのためではありません。この回避策の1つは、コピーを作成することです。

于 2012-09-12T15:37:46.080 に答える
1

JITがコードをどのように最適化するか(Javaのどの実装、どのOSなど)を言うのは難しいです。ここでの主なポイントは、for-each構文がイテレータ構文を使用するのと同じバイトコードにコンパイルされることです。ここここを参照してください。イテレータ構文を使用する主な理由は、反復中にアイテムを削除するためです。Iteratorインターフェースのremoveメソッドはオプションであることに注意してください。

バイトコードを自分で確認するために、いくつかの簡単な例を作成しました。

配列の反復ごとに

// compiled .class file is 585 bytes
public class ForEachLoop {
  public static void main(String[] args) {
    for(String s : args){
      System.out.println(s);
    }
  }
}

// byte code for main method
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=5, Args_size=1
   0:   aload_0
   1:   dup
   2:   astore  4
   4:   arraylength
   5:   istore_3
   6:   iconst_0
   7:   istore_2
   8:   goto    26
   11:  aload   4
   13:  iload_2
   14:  aaload
   15:  astore_1
   16:  getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   19:  aload_1
   20:  invokevirtual   #22; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   23:  iinc    2, 1
   26:  iload_2
   27:  iload_3
   28:  if_icmplt   11
   31:  return
  LineNumberTable: 
   line 4: 0
   line 5: 16
   line 4: 23
   line 7: 31

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      32      0    args       [Ljava/lang/String;
   16      7      1    s       Ljava/lang/String;

配列のインデックスループ

// compiled .class file is 554 bytes
public class ArrayLoop {
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) {
      System.out.println(args[i]);
    }
  }
}

// byte code for main method
public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=2, Args_size=1
   0:   iconst_0
   1:   istore_1
   2:   goto    17
   5:   getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   8:   aload_0
   9:   iload_1
   10:  aaload
   11:  invokevirtual   #22; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  iinc    1, 1
   17:  iload_1
   18:  aload_0
   19:  arraylength
   20:  if_icmplt   5
   23:  return
  LineNumberTable: 
   line 4: 0
   line 5: 5
   line 4: 14
   line 7: 23

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      24      0    args       [Ljava/lang/String;
   2      21      1    i       I

配列反復バイトコードがよりコンパクトであることがわかります(正確には31バイト)。

于 2012-09-12T15:46:33.240 に答える