4

Say, I've used a constant to allocate space for an array.

Later, when I access each position in the array using a for loop, should I address the test part using the .length value of the array or the constant value used during declaration?

In other words,

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

or

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

?

Which one is better?

5
  • 3
    Not likely to matter either way. Commented Mar 26, 2014 at 16:05
  • .length is an attribute, not a method. As it does not need to be computed (as can be Collection.size()), it is useless to introduce a new variable. Commented Mar 26, 2014 at 16:07
  • In Java an array's length value is a attribute, not a method, so either using .length or your constant should incur the same lookup time (theoretically, I could be wrong). Either way the amount of difference would be so minuscule that I doubt you'd see any visible performance gains. If you don't need index information or to modify the array when looping you should consider using the "foreach" construct: for (int i : array) { /* code */ } Commented Mar 26, 2014 at 16:08
  • Use array.length. It makes it clear what you are trying to do, and somebody reading the code does not have to go lookup how/where the variable is being used. Commented Mar 26, 2014 at 16:10
  • Thank you all. I was just wondering if there's something as a better practice concerning this. Thanks for your input! Commented Mar 26, 2014 at 16:14

5 Answers 5

5

Considering performance, none should be better than the other. Both should be in cache.

Considering readability, you should use array.length to make clear that you want to iterate over the array. So I would go for the first alternative.

Sign up to request clarification or add additional context in comments.

Comments

0

Since arrays are size-fixed, it doesn't matter which one you use. Unless you modified the reference of the array. Let's see this example:

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();
    }
}

Output:

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)

You are getting an exception, because you have changed the reference to an array with lower size. If you use arr.length, this problem won't happen. Unless you are sure you won't modify the reference of arr, you could use a constant.

Comments

0

Since array is immutable (I mean its length cannot be changed) this really does not matter from functional point of view.

From performance point of view both access to local variable and array.length is the same also.

This probably does matter if for example you have several arrays of the same length or you have any other reason that using array.length is less readable in specific context than using special variable.

1 Comment

Not fully correct, array.length is not a local variable, it is on the heap. However, it should be in cache so there will be no big difference.
0

The above 2 methods doesn't matters.

But if there is any calculations,

e.g

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

you should save that calculation into a variable, and use it in condition

like

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

For code optimization.

2 Comments

Whether your optimization yields any benefit is doubtful. The optimizer usually does that for you. But I agree that it also increases readability.
I just given the example,so that the OP should get an idea. Even now-a-days compilers are becoming intelligent, therefore it does optimization itself. But as a programmer we should not rely on compiler.
0

I think your best option is a foreach:

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

The compiler optimizes it for you, and it is easier to read. Hooray!


Optimizes, you say? Indeed. Usually it won't make a difference, but if it's easier to read AND faster, why not do it?

Consider this class:

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);
    }
}

Decompiling, we get this code:

  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   

So, if the array is a field, using length will load the field twice per iteration. This can make a difference if the field causes a flush (e.g., if it is volatile, or the JIT is in a bad mood, etc). It also isn't thread safe if the enclosing object is accessed by concurrent threads (the array can have changed between ops 7 and 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     

This does only one field access per iteration, but if the stars are right, it can still cost us. It also has rather unspecified behaviour if the field changes value.

Foreach does things slightly differently:

  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 

Only one field access, and one array length check! It is roughly equivalent to:

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

2 Comments

You can't modify the value for value types and you can't modify the array during the loop. The isn't a patch all solution. I'd be surprised if the compiler didn't try and optimize standard for loops in any way.
@izuriel it does optimize the code somewhat (very minor, but still). Also, of course it work if you modify the array, but it's far superior in terms of readibility for read-only loops (and slightly superior in performance).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.