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

Asked

Viewed 881 times

35

If I have an array or collection in a Arraylist and need to go through its elements, 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 single array:

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

However, I have seen in some forums1 They say that reporting the size outside of the loop condition is better, because at each iteration the loop does not need to be checking the list size 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...
}

There is even this difference in performance between reporting the list size outside or in the loop condition itself?

1 - I did not find the links at the time..

  • 1

    In the case of an array no, as length is a property, not a method, here has more details

  • 3

    +1, I read about in a performance book that talked about.

  • 1

    A simple but good question. I had never thought about it. I am now beginning to open my mind more to these details.

3 answers

29


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

Already did a test in the O.R.. The gain really is very low and it only pays to worry about it if the loop body is very fast, then the call of the method several times can weigh.

In the case of array There will be even less gain. Both access a variable directly, the only difference is that access to the property will require indirect access (via a pointer) to the object and access to the local variable will be done right there.

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

Optimizing

Nor do I say that there is any harm in always preferring to cache the value before. Unless the compiler or Jitter do some optimization by having the size checked inside the loop and when the size is checked off the optimization does not occur. Usually there is great gain in removing the access range overflow check within the loop if the compiler/Jitter can identify that it will never leave the limit. One of the ways to ensure this is to use a for each. Another is for the compiler to be smart and to verify that the condition plus use always produces contents within the range. The check is done inside the loop. What is outside 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 cache the size. Semantics is different in both cases.

You shouldn’t do this, but if you change the size of the data collection within the loop and the size was curled off it, it might not have the expected result. The die that indicates the end of the loop no longer matches the reality of the object.

I made an example showing the difference:

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

class Main {
    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));
    }
}

Behold as it gets in the ideone. And in the repl it.. Also put on the Github for future reference. Gives error by popping the existing track, because the loop is with a lagged information.

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

  • And how can I test this? Is there any way or the result is so tiny that you can’t reproduce and calculate the difference easily?

  • Test what? the difference between them? Saw the link that I put?

  • Oh yes, my inattention, I had not yet accessed and seen the test from there.

  • @Bigown, I’d like to make an addendum. Considering the best way to scan a List object is to use the Collection interfaces and their respective iterator() methods that in turn we can use the iterator.hasNext() to iterate with the elements of an Array or Arraylist.

18

Yes, even though the difference may be small.

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

In case it is a property like commented by @Marco, only changes position in memory so there is no difference.

Examples

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

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 should be taken to use direct calls in the loop. It can be changed with 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);

  • Accessing a property and a local variable is different.

  • The difference would be in access level, 1 direct loop, 2 loops "struct->property" ? would be almost 0, but I understand your criticism.

  • the difference would be an access or two access, no walk of loop. Virtually zero is not zero. I don’t know if you noticed that the question is about Java and not Javascript. In JS there may be different situations, including depending on the engine used.

4

Considering the following image:

inserir a descrição da imagem aqui


public class DemostrateFor {
    public static void main(String[] args){
        int ctr = 12;
        for( int j=10, k=14;
              j <= k;
              ++j, k=k-1, ctr++ )
        {
            System.out.println(j+":"+k+":"+ctr);
        }
    }
}


And considering also the very resource of language: O loop for melhorado.

for (String val : myList)
     System.out.println(val);

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


I believe that is it. Thank you for the question and for that, for the opportunity to learn.

Reference:

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.