Arraylist<Obj> java listing

Asked

Viewed 509 times

2

What is the advantage of using in a listing:

    for(Obj o : lista){
       // operacao
    }

instead of:

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

1 answer

5


I will talk here about the advantages of each of the strategies individually. There are situations where one is better than the other and vice versa.

But first, talk a little about the foreach.

In , the foreach is an "improved" iteration proposal for Java 5 1 2. In traditional vectors (no Collection, but int[] or Obj[]), the foreach is the same thing as an iteration by the index. Next, two examples of equivalent iterations:

Obj[] array = //... atributi o valor

// jeito "melhorado"
for (Obj o: array) {
    // faz algo...
}

// jeito "tradicional" que foi escondido pelo "foreach"
for (int i = 0; i < array.length(); i++) {
    Obj o = array[i];
    // faz algo
}

In the case for elements of the world Collections, the situation is a little more complex. These elements (such as ArrayList, HashSet, LinkedList, TreeSet ...) are elements everlasting. And Java has a special markup for these elements: they implement the interface Iterable.

In this particular case, the "traditional" iteration could not follow an index. In many cases, because there was no index to follow java8-hashset). In others, because following an index was an inefficient alternative (LinkedList). For these cases, since Java 2, it was necessary to iterate through iterators old-school. And the foreach for the collections (actually, for everything that implements Iterable) is a syntactic sugar that, after being compiled, ends up having an iterator old-school below. See example extracted of that reply by Soen:

List<String> someList = // ... povoa a lista com os valores adequados ...

// método melhorado
for (String item : someList) {
    System.out.println(item);
}

// método old-school
for (Iterator<String> i = someList.iterator(); i.hasNext();) {
    String item = i.next();
    System.out.println(item);
}

So the foreach in this context serves to reduce the complexity of the code written by the programmer.

Advantages of foreach

  1. Appropriate access method for the appropriate structure
    In the case of a ArrayList, access an element at position i is instantaneous. As instantaneous as doing, for a vector Obj[] array, array[i].
    In the case of a LinkedList, however, the conversation is different. Accessing arbitrary position i requires to leave the root of the list and count i nodes (including root). This means they are needed i jumps in memory. Then, the following code has linear time to ArrayList, however quadratic to LinkedList:

    List<String> l = // ... povoa a lista ...
    for (int i = 0; i < l.size(); i++) {
        System.out.println(l.get(i));
    }
    

    In the case of LinkedList, the Iterator can be done intelligently, in which it is stored which current node of the list to, when calling next, go to the next node. As each iterator is called a single time, it has its value element extracted immediately and only then do the jump to the next node, the foreach ensures that the following code runs in linear time for any list:

    List<String> l = // ... povoa a lista ...
    for (String e: l) {
        System.out.println(e);
    }
    
  2. Code concision and clarity of the past message
    When using the foreach, the person who reads your code and who is used to Java will instantly recognize that you need to go through all elements (or even one in particular, in case there is flow deviation, such as break, or return, or exception release). It will also know immediately which variable will load the value being iterated. Without blinking, he’ll just bat his eye and realize all this.
    When you iterate from the index, you need to do the redemption code, and sometimes it can get a little confusing if you don’t do it carefully:

    List<String> l = // ... povoa a lista ...
    for (int i = 0; i < l.size(); i++) {
        String e = l.get(i);
        System.out.println(e);
    }
    

    Here, in a code made without much care, the reader needs to look with a little more discretion to know what is the value of e at every moment.

  3. Guarantee against typos
    I even accidentally made a typo the first time I was writing the examples here:

    List<String> l = // ... povoa a lista ...
    for (int i = 0; i < l.size(); l++) {
        System.out.println(l.get(i));
    }
    

    Yes, I said "increase" on l, whereas l is a list and therefore I would get a compilation error in this code.

  4. Guarantee against logic errors
    Imagine that you, as a smart boy, try to optimize the writing of for more intelligently. You will at the same time increase the index and take the new value for the variable e:

    List<String> l = // ... povoa a lista ...
    for (int i = 0, String e = l.get(0); i < l.size(); i++, e = l.get(i)) {
        System.out.println(e);
    }
    

    Did you identify the logic error? No? Well, here we would have obtained an exception: IndexOutOfBounds. That’s because, after the execution of the last tie, I’m doing e = l.get(i), and at this time, i == l.size(). Like l is indexed starting with zero, which means that p last element is necessarily in the index l.size() - 1.

  5. Immutable collection contract
    This detail here is somewhat more subtle...
    When you’re wearing one Iterator, change the collection it refers to without using the special commands of Iterator, causes in a ConcurrentModificationException. This means that if you do not warn the iterator, you can’t alter the structure of the eternal. By changing structure, you can add/remove structural elements from the list or even set a new value to a pre-existing structural element.
    As in the foreach you don’t have the least access to Iterator that is under the table, you can not change the collection. It means that the list will always be the same as when you started.

Advantages offor via index

  1. Pair index with element
    If you want to pair up using foreach, you need to keep a variable on the outside, updating it every time a new loop starts. When you do the direct rescue knowing the index, the ratio between the index i and the element e simply exists.

  2. Ability to change value in the list
    Well, if you want to change the value (insert a new object) at the position i of the list, based on the previous object of that same position i, then all you have to do is use this loop. Or use the Iterator well old-school. And good luck trying to explain to the project colleagues what that was.

  3. Performance for specific cases
    There may be cases where accessing the index is more advantageous in terms of computational performance. I particularly know only one case in which this is likely to occur: when the class Iterable provides access to your inner vector. In this case, take the vector and access position i avoids a method call:

    MyListExposable<String> l = // ... povoa a lista ...
    String[] v = l.classicArray;
    for (int i = 0; i < v.length; i++) {
        String e = v[i];
        System.out.println(e);
    }
    

    Note that even so, it is possible that the gain is marginal or even negligible.

  4. Specific search order
    Maybe you need to iterate the elements from last to first? In this case, maybe your iterable won’t provide you with a ready-made iterator. But if it is something indexable, you can easily define which index will start, how will the index evolve given the current state of the index and much more.
    Still, any gain from it is questionable.

Completion

In the general case, precisely by demeter’s law, the use of foreach gets a performance o(n) in most cases (well implemented classes). However, if there is a guarantee of knowing exactly which implementation will be passed, maybe it is possible to obtain a better performance by the indexed search (a asymptotic complexity remains the same at best).

Use the foreach also makes you have less to write. Less writing, less bugs. The more you need the programmer to manage, the more likely it is to pass a typo or a subtle logic error. Hiding the complexity behind a code makes it more "declarative" and less "imperative" 5. For reasons of maintenance and code reading, it is often better to focus on "WHAT" than on "HOW", and more imperative writing invariably focuses more on "HOW" 6.

Use the foreach is also a strategy to make a "fixed list computation". This means that the elements passed in the list will be checked and they cannot be exchanged during directly during the loop. It is worth noting that follow the principle of fail-fast ensures that every change in the list will generate an error in the next iteration, thus trying to maintain the integrity of what is working.

Use a for indexed will already provide you, immediately, the element and its associated index. Unlike the foreach, where to maintain the associated index an external control would be required. And foreach it is also not guaranteed that the i-nth element actually belongs to the index i, since the iterator order is arbitrary.


Links used:

Browser other questions tagged

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