Is there a difference between reporting the size of the loop condition or outside it?

34

If I have an array or collection in a Arraylist and need to traverse its elements, I occasionally need to make use of loop repetition.

Ex:

for(int i = 0; i < arrayList.size(); i++){
  //iteração...
}

or in the case of a simple array:

for(int i = 0; i < vetor.length; i++){
  //iteração...
}

However, I have seen in some forums 1 that reporting size outside the loop condition is better, because with each iteration the loop does not have to be checking the size of the list and consequently making it fast. Something like:

int tamanho = arrayList.size();

for(int i = 0; i < tamanho; i++){
  //iteração...
}

//no caso do array

int tamanho = vetor.length;

for(int i = 0; i < tamanho; i++){
  //iteração...
}

Is there even such a difference in performance between informing the size of the list outside or in the loop's own condition?

1 - I did not find the links right now. .

    
asked by anonymous 13.09.2016 / 15:45

3 answers

28

In general the gain will be minimal in the case of ArrayList that calls a method and has some cost.

Have tested the OS . The gain really is very low and it only pays to worry about it if the body of the loop is very fast, then the method call several times can weigh.

In the case of array there will be a smaller gain still. Both access a variable directly, the only difference is that access to the property will require indirect access (through a pointer) to the object and access to the local variable will be done directly there.

I think not in Java, but in C # even a property would be done inline having the same gain as array .

Optimization

I also do not say that there is any harm in always prefer to cache the value before. Unless the compiler or JITter does some optimization by having the size checked inside the loop and when the size is checked out the optimization does not occur. There is usually a big gain in removing the access-band overflow check inside the loop if the / JITter compiler can identify that it will never go out of bounds. One way to ensure this is to use for each . Another is the compiler being smart and checking that the condition plus usage always produces indexes within the range. The check is done inside the loop. What is out of the loop is not reliable information, it may even give error as shown below.

Semantics

But note that in addition to performance you may have a better reason to do the size cache. The semantics are different in both cases.

You should not do this, but if you change the size of the data collection inside the loop and the size has been cached outside of it, it may not have the expected result. The data that indicates the end of the loop no longer beats the reality of the object.

I gave an example showing the difference:

import java.util.*;
import java.lang.*;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        ArrayList<Integer> x = new ArrayList<Integer>();
        x.add(1); x.add(2); x.add(3); x.add(4); x.add(5);
        for (int i = 0; i < x.size(); i++) {
            if (i % 2 == 0) { //retira os elementos pares
                x.remove(i);
            }
            System.out.println("Tamanho atual = " + x.size() + ", i = " + i); //só para ajudar visualizar
        }
        System.out.println("---");
        for (int i = 0; i < x.size(); i++) {
            System.out.println(x.get(i));
        }
        System.out.println("---------------------");
        x = new ArrayList<Integer>();
        x.add(1); x.add(2); x.add(3); x.add(4); x.add(5);
        int tamanho = x.size();
        for (int i = 0; i < tamanho; i++) {
            if (i % 2 == 0) {
                x.remove(i);
            }
            System.out.println("Tamanho atual = " + x.size() + ", i = " + i); //só para ajudar visualizar
        }
        System.out.println("---");
        for (int i = 0; i < x.size(); i++) {
            System.out.println(x.get(i));
        }
    }
}

See how it looks in ideone . That gives error by bursting the existing band, because the loop is with an information lagged.

There is another problem with different semantics that is not the case with the question, but it is worth the information. You might use a method to get a value and decide whether to close the loop. This method can have a side effect. Is it desirable for this effect to occur at each iteration or should it only occur once? This is important.

    
13.09.2016 / 15:58
16

Yes, even though the difference may be minimal.

The logic is simple, comparing a value will be faster than having to execute a method to get the value.

In case it is a property such as #, it only changes the position in the memory so there is no difference.

p> Examples

  • Let's say you have a len() method that returns the number of characters in a string and it takes (hypothetically) 1.5ms to execute.
  • Let's say the comparison takes 0.5ms to execute.

1

var str = 'name';
for(var i = 0; i < len(str); i++){ 
    // AQUI VOCÊ TERA UM TOTAL DE 8ms DE EXECUÇÃO.
    // 6ms = 4x1.5 das 4 chamadas de len().
    // 2ms = 4x0.5 das 4 comparações. 
}

2

var str = 'name';
var len = len(str);
for(var i = 0; i < len; i++){ 
    // AQUI VOCÊ TERA UM TOTAL DE 3.5ms DE EXECUÇÃO
    // 1.5ms = 1x1.5 da 1 chamadas de len().
    // 2ms = 4x0.5 das 4 comparações. 
}

Addendum

Care must be taken when using direct calls on the loop. It can be changed at each iteration.

Example

var mask = '###########';
function countChar(str, char){
	return str.split(char).length-1;
}
for(var i = 0; i < countChar(mask, '#'); i++){ // AQUI A COMPARAÇÃO VAI DIMINUINDO A CADA LAÇO POIS SEMPRE É CHAMADO O MÉTODO.
	var split = mask.split('');
    split[i] = 1;
    mask = split.join('');
}
console.log(mask);
    
13.09.2016 / 16:09
2

Considering the following image:

publicclassDemostrateFor{publicstaticvoidmain(String[]args){intctr=12;for(intj=10,k=14;j<=k;++j,k=k-1,ctr++){System.out.println(j+":"+k+":"+ctr);
        }
    }
}

And also considering the language feature itself: O loop for melhorado .
for (String val : myList)
     System.out.println(val);

So, yes: there is a difference between reporting the size in the termination condition of loop for . The loop performance is directly affected in case we report the termination condition wrongly, that is, calling a method at each iteration, java solving this internally using% with%. This way freeing the programmer to worry about the recursion or improvement in the logic to be used in the iterations.

I think that's it. Thanks for the question and for this, for the opportunity to learn.

Reference:

[MALA GUPTA, 2013], OCA Java SE 7 Programmer I
Certification Guide : PREPARE FOR THE 1Z0-803 EXAM

    
31.03.2017 / 09:59