在Java中定义变量inside vs outside?

时间:2013-11-23 23:37:11

标签: java iteration micro-optimization

假设我们有一个带有大量迭代的双循环,我们是否应该在循环外定义变量以加快速度?仅举例:

for(int i=0;i<2000;i++)
     for(int j=0;j<1000;j++)
          System.out.println(i+j);

因为我每次更改值时都会初始化j,所以最好将j声明为外部,例如:

 for(int i=0;i<2000;i++){
     int j=0;
     for(j=0;j<1000;j++)
          System.out.println(i+j); 
  }

对于我自己,我喜欢第一种方式,它更具可读性。但是,我不确定第二种方式是否会加快程序的速度?

3 个答案:

答案 0 :(得分:1)

不,第二种形式在任何方面都不是更好(它甚至不会编译,因为你错过了for循环的init-section)!

for语句构建如下:for (init, expression, update) statement

  • init语句将在循环之前执行ONCE 开始

  • expression语句将在每次迭代之前执行 循环。只有当结果为true时,循环才会继续。

  • update语句将在每次迭代后立即执行。

  • statement是将在每个中执行的语句 迭代。

所以它只是一个简短形式:

init;
while (expression) {
    statement;
    update;
}

由于init只会被执行一次,如果你把它放在for循环之前就没有优化。

答案 1 :(得分:1)

Javac生成的代码完全相同,因此没有任何区别:

给出“Test.java”文件:

public class Test {

    public static int a(int[][] v) {
        int sum = 0;
        int rows = v.length;
        int cols = v[0].length; // yes, this fails if v[0] is null
        for (int j=0; j<rows; j++) {
            for (int i=0; i<cols; i++) {
                sum += v[j][i];
            }           
        }
        return sum;
    }

    public static int b(int[][] v) {
        int sum = 0;
        int rows = v.length;
        int cols = v[0].length; // yes, this fails if v[0] is null
        int j, i;
        for (j=0; j<rows; j++) {
            for (i=0; i<cols; i++) {
                sum += v[j][i];
            }           
        }
        return sum;
    }
}

编译(javac Test.java)并查看类文件中的字节码(javap -c Test的结果)。这是b

public static int b(int[][]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   aload_0
   3:   arraylength
   4:   istore_2
   5:   aload_0
   6:   iconst_0
   7:   aaload
   8:   arraylength
   9:   istore_3
   10:  iconst_0
   11:  istore  4
   13:  iload   4
   15:  iload_2
   16:  if_icmpge   50
   19:  iconst_0
   20:  istore  5
   22:  iload   5
   24:  iload_3
   25:  if_icmpge   44
   28:  iload_1
   29:  aload_0
   30:  iload   4
   32:  aaload
   33:  iload   5
   35:  iaload
   36:  iadd
   37:  istore_1
   38:  iinc    5, 1
   41:  goto    22
   44:  iinc    4, 1
   47:  goto    13
   50:  iload_1
   51:  ireturn

答案 2 :(得分:0)

没有必要这样做。内置类型没有显着的设置成本。

事物与物体不同。

出于性能原因,您应该在循环外预先声明对象。我用JDK 1.4测试了这个,从那时起再也没有。可能更新的JDK会优化它。

for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
      String x = "Hello " + j;

明显慢于(定义外部循环)

String x;
for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
      x = "Hello " + j;

明显慢于(最终循环)

for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
       final String x = "Hello " + j;

更新:只使用JDK 7u45,1000 x 1000次迭代运行时间

我已将代码设置为多次运行循环并将变量保存在结果数组中。另一次运行不会将变量存储在循环之外。

  • 循环中的字符串分配和数组存储:Avg 109.49 / NoGC:47-49(*)
  • 循环中没有商店的字符串分配:平均43.5 / NoGC:39-47

  • 循环外的字符串分配和数组存储:Avg 98.9 / NoGC:42-44

  • 循环外的字符串分配,没有商店:平均28.25 / NoGC:27-32

  • 循环中的最终字符串分配和数组存储:Avg 98.92 / NoGC:42-50

  • 循环中没有商店的最终字符串分配:Avg 28.31 / NoGC:27-34

结果显示,对象的声明会造成损失。但真正的驱动力是GC。如果在循环外部使用该值,则循环内部或外部的声明效果会很小。在循环中使用最终的String将没有任何区别。 如果你不在循环之外存储,那么在循环内部或外部变量的声明之间的区别是显着的,最终,再次,没有区别。

(*存储所有值时,大部分时间都在GC运行中丢失,这就是我在没有GC的情况下添加运行范围的原因。)