java for循环的效率

时间:2012-09-12 15:31:44

标签: java memory-management loops for-loop

作为这个问题的附录Java loop efficiency ("for" vs. "foreach")

我有一个简单的问题。增强的for循环是否具有更大的内存占用,或者它们是否都编译为相同的结构,使它们的内存占用相同,从而使for(Object o : collection) { ... }总是更好地进行只读操作?

我的理由是,在我正在进行的模拟器中,每帧(60 /秒)我正在对数组中可能有数千个项目进行操作,例如绘制面部,光线追踪等等。

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

看起来它可能会制作该系列的记忆副本(而且我似乎记得它在我之前的阅读中有所作为),这在大多数情况下是可以的,但如果你每秒做300次光线追踪,那就不行了。

另一方面,很明显循环

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

通过引用完成所有事情并且占用空间相对较小,即使它更难阅读(但老实说,并不多)

任何想法,伙计们?

(注意:读取循环难度的影响只有当你进入多个层时才会明显 - 对于单层fors而言,增益非常小。我从经验中说出来...... 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]。)

2 个答案:

答案 0 :(得分:1)

如果你有一个使用hte的ArrayList,那么下面的模式可以是微优化

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

您保存的内存大约为16-24个字节,因为for-each循环将始终创建一个迭代器。

  

(60 /秒)我正在对数组中可能有数千个项目进行操作,例如绘制面部,光线追踪,

对于每秒60,000,您不太可能注意到差异,因为您对每个项目进行了相对重要的工作。

  

看起来它可能会成为集合的内存副本

如果您在循环时更改列表,那么这就是为什么您可以获得ConcurrentModicationException的原因。一个解决方法是复制。

答案 1 :(得分:1)

很难说JIT将如何优化您的代码(Java的哪个实现,哪个操作系统等)。这里的要点是for-each语法编译为与使用迭代器语法相同的字节代码,请参阅herehere。使用迭代器语法的主要原因是在迭代期间删除项目。请注意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个字节)。