Façons d'itérer une liste en Java

étant un peu nouveau dans le langage Java, j'essaie de me familiariser avec toutes les façons (ou au moins les non-pathologiques) qu'on pourrait itérer à travers une liste (ou peut-être d'autres collections) et les avantages ou inconvénients de chacune.

avec un objet List<E> list , je connais les façons suivantes de boucler tous les éléments:

de Base pour boucle (bien sûr, il y a l'équivalent while / do while boucles ainsi)

// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
    E element = list.get(i);
    // 1 - can call methods of element
    // 2 - can use 'i' to make index-based calls to methods of list

    // ...
}

Note: comme @amarseillan l'a souligné, ce formulaire est un mauvais choix pour itérer plus de List s, parce que la mise en œuvre effective de la méthode get n'est peut-être pas aussi efficace que la méthode Iterator . Par exemple, les implémentations LinkedList doivent traverser toutes les les éléments qui précèdent i pour obtenir l'élément i-ème.

Dans l'exemple ci-dessus il y a la mise en œuvre de List ne peut en aucun cas "sauvegarder sa place" pour rendre les itérations futures plus efficaces. Pour un ArrayList cela n'a pas vraiment d'importance, parce que la complexité/coût de get est temps constant (O(1)) tandis que pour un LinkedList est proportionnel à la taille de la liste (O(n)).

pour plus d'information sur la complexité computationnelle des implémentations Collections , consultez cette question .

Renforcée pour la boucle (gentiment expliqué dans cette question )

for (E element : list) {
    // 1 - can call methods of element

    // ...
}

Itérateur

for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list

    // ...
}

ListIterator

for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list
    // 3 - can use iter.add(...) to insert a new element into the list
    //     between element and iter->next()
    // 4 - can use iter.set(...) to replace the current element

    // ...
}

Fonctionnel Java

list.stream().map(e -> e + 1); // Can apply a transformation function for e

itérable.forEach , Flux.forEach ,...

(une méthode de carte à partir de L'API Stream de Java 8 (voir la réponse de @i_am_zero).)

en Java 8 Les classes de collection qui implémentent Iterable (par exemple, toutes les List s) ont maintenant une méthode forEach , qui peut être utilisée à la place du pour l'énoncé de boucle démontré ci-dessus. (Voici une autre question qui fournit une bonne comparaison.)

Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
//     (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
//     being performed with each item.

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).

y a-t-il d'autres moyens, le cas échéant?

(BTW, mon intérêt ne découle pas du tout d'un désir de optimiser la performance ; je veux juste savoir quelles formes sont disponibles pour moi en tant que développeur.)

463
demandé sur Peter Mortensen 2013-08-23 23:00:14

11 réponses

les trois formes de boucle sont presque identiques. La boucle améliorée for :

for (E element : list) {
    . . .
}

est, selon le Java Language Specification , à l'identique , en effet, à l'utilisation explicite d'un itérateur avec un traditionnel for en boucle. Dans le troisième cas, vous ne pouvez modifier le contenu de la liste qu'en supprimant l'élément courant, et seulement si vous le faites par la méthode remove de la itérateur lui-même. Avec l'indice de l'itération, vous êtes libre de modifier la liste en aucune façon. Cependant, l'ajout ou la suppression d'éléments qui précèdent l'index courant risque de faire sauter des éléments de votre boucle ou de traiter le même élément plusieurs fois; vous devez ajuster l'index de boucle correctement lorsque vous apportez de telles modifications.

dans tous les cas, element renvoie à l'élément de la liste. Aucune des méthodes d'itération fait une copie de n'importe quoi dans la liste. Les changements de l'état interne de element sera toujours vu dans l'état interne de l'élément correspondant de la liste.

essentiellement, il n'y a que deux façons d'itérer une liste: en utilisant un index ou un itérateur. La boucle enhanced for n'est qu'un raccourci syntaxique introduit en Java 5 pour éviter l'ennui de définir explicitement un itérateur. Pour les deux styles, vous pouvez trouver des variations essentiellement triviales en utilisant for , while ou do while blocs, mais elles se résument toutes à la même chose (ou, plutôt, de deux choses).

EDIT: @iX3 souligne dans un commentaire, vous pouvez utiliser un ListIterator pour définir l'élément courant de la liste que vous l'itération. Vous devez utiliser List#listIterator() au lieu de List#iterator() pour initialiser la variable de boucle (qui, évidemment, devrait être déclaré un ListIterator plutôt qu'un Iterator ).

215
répondu Ted Hopp 2013-12-15 21:01:52

exemple de chaque type énuméré dans la question:

ListIterationExample.java

import java.util.*;

public class ListIterationExample {

     public static void main(String []args){
        List<Integer> numbers = new ArrayList<Integer>();

        // populates list with initial values
        for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
            numbers.add(i);
        printList(numbers);         // 0,1,2,3,4,5,6,7

        // replaces each element with twice its value
        for (int index=0; index < numbers.size(); index++) {
            numbers.set(index, numbers.get(index)*2); 
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // does nothing because list is not being changed
        for (Integer number : numbers) {
            number++; // number = new Integer(number+1);
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14  

        // same as above -- just different syntax
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            number++;
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // ListIterator<?> provides an "add" method to insert elements
        // between the current element and the cursor
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.add(number+1);     // insert a number right before this
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

        // Iterator<?> provides a "remove" method to delete elements
        // between the current element and the cursor
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            if (number % 2 == 0)    // if number is even 
                iter.remove();      // remove it from the collection
        }
        printList(numbers);         // 1,3,5,7,9,11,13,15

        // ListIterator<?> provides a "set" method to replace elements
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.set(number/2);     // divide each element by 2
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7
     }

     public static void printList(List<Integer> numbers) {
        StringBuilder sb = new StringBuilder();
        for (Integer number : numbers) {
            sb.append(number);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length()-1); // remove trailing comma
        System.out.println(sb.toString());
     }
}
39
répondu iX3 2013-08-23 21:09:38

la boucle de base n'est pas recommandée car vous ne connaissez pas l'implémentation de la liste.

si c'était une liste de liens, chaque appel à

list.get(i)

serait itératif sur la liste, résultant en n^2 complexité de temps.

19
répondu amarseillan 2015-04-08 19:22:19

a JDK8-style itération:

public class IterationDemo {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream().forEach(elem -> System.out.println("element " + elem));
    }
}
15
répondu eugene82 2014-04-06 17:01:47

dans Java 8 nous avons plusieurs façons d'itérer sur les classes de collecte.

à l'Aide de Itérable forEach

les collections qui implémentent Iterable (par exemple toutes les listes) ont maintenant la méthode forEach . Nous pouvons utiliser méthode-référence introduit en Java 8.

Arrays.asList(1,2,3,4).forEach(System.out::println);

à l'Aide de Flux de forEach et forEachOrdered

nous pouvons aussi itérer sur une liste à l'aide de Flux :

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);

nous devrions préférer forEachOrdered à forEach parce que le comportement de forEach est explicitement non-déterministe où comme le forEachOrdered effectue une action pour chaque élément de ce flux, dans l'ordre de rencontre du flux si le flux a un ordre de rencontre défini. Ainsi forEach ne garantit pas que la commande serait maintenue.

L'avantage avec streams est que nous pouvons également utiliser des streams parallèles lorsque cela est approprié. Si l'objectif est uniquement d'imprimer les éléments indépendamment de l'ordre alors nous pouvons utiliser courant parallèle:

Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
5
répondu i_am_zero 2018-02-25 02:08:09

Je ne sais pas ce que vous considérez comme pathologique, mais laissez-moi vous fournir quelques alternatives que vous n'auriez pas pu voir avant:

List<E> sl= list ;
while( ! sl.empty() ) {
    E element= sl.get(0) ;
    .....
    sl= sl.subList(1,sl.size());
}

ou sa version récursive:

void visit(List<E> list) {
    if( list.isEmpty() ) return;
    E element= list.get(0) ;
    ....
    visit(list.subList(1,list.size()));
}

aussi, une version récursive du Classique for(int i=0... :

void visit(List<E> list,int pos) {
    if( pos >= list.size() ) return;
    E element= list.get(pos) ;
    ....
    visit(list,pos+1);
}

je les mentionne parce que vous êtes" un peu nouveau en Java " et cela pourrait être intéressant.

4
répondu Mario Rossi 2013-08-23 19:59:29

vous pouvez utiliser forEach à partir de Java 8:

 List<String> nameList   = new ArrayList<>(
            Arrays.asList("USA", "USSR", "UK"));

 nameList.forEach((v) -> System.out.println(v));
2
répondu Sudip Bhandari 2018-02-23 17:28:03

pour une recherche en arrière, vous devez utiliser ce qui suit:

for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
    SomeClass item = iterator.previous();
    ...
    item.remove(); // For instance.
}

si vous voulez connaître une position, utilisez iterator.previousIndex (). Cela aide aussi à écrire une boucle interne qui compare deux positions dans la liste (les itérateurs ne sont pas égaux).

0
répondu CoolMind 2016-11-25 13:12:18

Dans java 8 vous pouvez utiliser List.forEach() méthode lambda expression pour itérer sur une liste.

import java.util.ArrayList;
import java.util.List;

public class TestA {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("Apple");
        list.add("Orange");
        list.add("Banana");
        list.forEach(
                (name) -> {
                    System.out.println(name);
                }
        );
    }
}
0
répondu pippi longstocking 2018-09-06 13:34:26

D'accord, de nombreuses alternatives sont énumérées. Le plus facile et le plus propre serait d'utiliser simplement l'énoncé amélioré for comme ci-dessous. Le Expression est d'un certain type qui est itérable.

for ( FormalParameter : Expression ) Statement

par exemple, pour itérer, List ids, nous pouvons simplement ainsi,

for (String str : ids) {
    // Do something
}
-1
répondu Yu Chen 2018-02-23 17:26:33

vous pouvez toujours désactiver les premier et troisième exemples avec une boucle de temps et un peu plus de code. Cela vous donne l'avantage de pouvoir utiliser le do-while:

int i = 0;
do{
 E element = list.get(i);
 i++;
}
while (i < list.size());

bien sûr, ce genre de chose pourrait causer une NullPointerException si la liste.size () renvoie 0, parce qu'il est toujours exécuté au moins une fois. Cela peut être corrigé en testant si l'élément est nul avant d'utiliser ses attributs / méthodes tho. Pourtant, il est beaucoup plus simple et plus facile à utiliser la boucle pour

-2
répondu shieldgenerator7 2013-08-23 19:07:36