For循环条件:数组大小- .length或常量



我使用了一个常量来为数组分配空间。

之后,当我使用for循环访问数组中的每个位置时,我应该使用数组的.length值或声明期间使用的常量值来处理测试部分吗?

也就是说

for (int i = 0; i < array.length; i++)

for (int i = 0; i < constantArraySize; i++)

?

哪个更好?

考虑性能,没有一个应该比另一个更好。

考虑到可读性,您应该使用array.length来明确希望遍历数组。所以我会选择第一个选项

由于数组的大小是固定的,所以使用哪一个并不重要。除非你修改了数组的引用。让我们看一下这个例子:

public class Test
{
    final int SIZE = 3;
    int[] arr;
    public Test()
    {
        arr = new int[SIZE];
        print();
        arr = new int[SIZE - 1]; // modify
        print();
    }
    public void print()
    {
        for (int i = 0; i < SIZE; i++) {
            System.out.println(arr[i]);
        }
    }
    public static void main(String[] args)
    {
        Test t = new Test();
    }
}
输出:

0
0
0
0
0
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
    at testpackage.Test.print(Test.java:19)
    at testpackage.Test.<init>(Test.java:13)
    at testpackage.Test.main(Test.java:25)

您将得到一个异常,因为您已将引用更改为较小大小的数组。如果你使用arr.length,这个问题就不会发生。除非您确定不会修改arr的引用,否则您可以使用常量

由于数组是不可变的(我的意思是它的长度不能改变),从功能的角度来看,这真的无关紧要。

从性能的角度来看,对局部变量和array.length的访问也是相同的。

这可能很重要,例如,如果你有几个相同长度的数组,或者你有任何其他原因,使用array.length在特定上下文中比使用特殊变量可读性差。

以上两个方法都不重要。

但如果有任何计算,

for(int i=0;i< (j*10+10);i++)
{
}

你应该将计算保存到一个变量中,并在条件

中使用它

int temp=j*10+10;
for(int i=0;i<temp;i++)
{
}

代码优化

我认为你最好的选择是foreach:

for (Item item : array) {
    // Do stuff
}

编译器会为您优化它,并且它更容易阅读。万岁!


优化,你说?确实。通常不会有什么不同,但如果它更容易阅读和更快,为什么不这样做呢?

考虑这个类:

final int SIZE = 3;
final String[] a = new String[SIZE];
void doStuff(String s){}
void useLength(){
    for (int i = 0; i < a.length; i++) {
        doStuff(a[i]);
    }
}
void useConstant(){
    for (int i = 0; i < SIZE; i++) {
        doStuff(a[i]);
    }
}
void useForEach(){
    for (String s : a) {
        doStuff(s);
    }
}

进行反编译,得到如下代码:

  void useLength();
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: aload_0       
       4: getfield      #4                  // Field a:[Ljava/lang/String;
       7: arraylength   
       8: if_icmpge     27
      11: aload_0       
      12: aload_0       
      13: getfield      #4                  // Field a:[Ljava/lang/String;
      16: iload_1       
      17: aaload        
      18: invokevirtual #5                  // Method doStuff:(Ljava/lang/String;)V
      21: iinc          1, 1
      24: goto          2
      27: return   

因此,如果数组是一个字段,使用length将在每次迭代中加载该字段两次。如果字段导致刷新(例如,如果它是易失的,或者JIT心情不好,等等),这可能会产生影响。如果封闭对象被并发线程访问,它也不是线程安全的(数组可以在op 7和op 17之间被更改)。

  void useConstant();
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: iconst_3      
       4: if_icmpge     23
       7: aload_0       
       8: aload_0       
       9: getfield      #4                  // Field a:[Ljava/lang/String;
      12: iload_1       
      13: aaload        
      14: invokevirtual #5                  // Method doStuff:(Ljava/lang/String;)V
      17: iinc          1, 1
      20: goto          2
      23: return     

每次迭代只访问一个字段,但如果星型是正确的,它仍然会花费我们。如果字段改变值,它也有相当未指定的行为。

Foreach的操作略有不同:

  void useForEach();
    Code:
       0: aload_0       
       1: getfield      #4                  // Field a:[Ljava/lang/String;
       4: astore_1      
       5: aload_1       
       6: arraylength   
       7: istore_2      
       8: iconst_0      
       9: istore_3      
      10: iload_3       
      11: iload_2       
      12: if_icmpge     32
      15: aload_1       
      16: iload_3       
      17: aaload        
      18: astore        4
      20: aload_0       
      21: aload         4
      23: invokevirtual #5                  // Method doStuff:(Ljava/lang/String;)V
      26: iinc          3, 1
      29: goto          10
      32: return 

只有一个字段访问和一个数组长度检查!它大致相当于:

void useForEach(){
    String[] localArr = arr;
    for (int i = 0, length = arr.length; i < length; i++) {
        doStuff(localArr[i]);
    }
}

最新更新