Étant un peu nouveau dans le langage Java, j'essaie de me familiariser avec toutes les façons (ou au moins celles non pathologiques) que l'on pourrait parcourir à travers une liste (ou peut-être d'autres collections) et les avantages ou les inconvénients de chacun.
Étant donné un List<E> list
objet, je connais les façons suivantes de parcourir tous les éléments:
De base pour la boucle (bien sûr, il existe également des boucles while
/ équivalentes do while
)
// 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
// ...
}
Remarque: Comme @amarseillan l'a souligné, ce formulaire est un mauvais choix pour itérer sur List
s, car l'implémentation réelle de la get
méthode peut ne pas être aussi efficace que lors de l'utilisation d'un Iterator
. Par exemple, les LinkedList
implémentations doivent traverser tous les éléments précédant i pour obtenir le i-ème élément.
Dans l'exemple ci-dessus, l' List
implémentation n'a aucun moyen de "sauver sa place" pour rendre les futures itérations plus efficaces. Pour un, ArrayList
cela n'a pas vraiment d'importance, car la complexité / le coût de get
est un temps constant (O (1)) tandis que pour un LinkedList
il est proportionnel à la taille de la liste (O (n)).
Pour plus d'informations sur la complexité de calcul des Collections
implémentations intégrées , consultez cette question .
Amélioré pour la boucle (bien 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
// ...
}
Java fonctionnel
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach , ...
(Une méthode de carte de l'API Stream de Java 8 (voir la réponse de @ i_am_zero).)
Dans Java 8, les classes de collection qui implémentent Iterable
(par exemple, tous les List
s) ont désormais une forEach
méthode, qui peut être utilisée à la place de l' instruction for loop illustrée 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).
Quels autres moyens existe-t-il, le cas échéant?
(BTW, mon intérêt ne découle pas du tout d'un désir d' optimiser les performances ; je veux juste savoir quelles formes sont à ma disposition en tant que développeur.)
List
interface est-elle spécifique?Réponses:
Les trois formes de bouclage sont presque identiques. La
for
boucle améliorée :est, selon la spécification du langage Java , identique en effet à l'utilisation explicite d'un itérateur avec une
for
boucle traditionnelle . Dans le troisième cas, vous ne pouvez modifier le contenu de la liste qu'en supprimant l'élément courant et, ensuite, uniquement si vous le faites via laremove
méthode de l'itérateur lui-même. Avec l'itération basée sur un index, vous êtes libre de modifier la liste de n'importe quelle manière. Cependant, l'ajout ou la suppression d'éléments antérieurs à l'index actuel risque de faire sauter vos éléments en boucle ou de traiter le même élément plusieurs fois; vous devez ajuster correctement l'index de boucle lorsque vous effectuez de telles modifications.Dans tous les cas,
element
est une référence à l'élément de liste réel. Aucune des méthodes d'itération ne copie quelque chose de la liste. Les modifications de l'état interne deelement
seront toujours visibles dans l'état interne de l'élément correspondant de la liste.Essentiellement, il n'y a que deux façons d'itérer sur une liste: en utilisant un index ou en utilisant un itérateur. La boucle for améliorée n'est qu'un raccourci syntaxique introduit dans Java 5 pour éviter l'ennui de définir explicitement un itérateur. Pour les deux styles, vous pouvez proposer des variations essentiellement triviales en utilisant
for
,while
ou desdo while
blocs, mais elles se résument toutes à la même chose (ou, plutôt, deux choses).EDIT: Comme @ iX3 le fait remarquer dans un commentaire, vous pouvez utiliser a
ListIterator
pour définir l'élément actuel d'une liste pendant que vous itérez. Vous auriez besoin d'utiliserList#listIterator()
au lieu deList#iterator()
pour initialiser la variable de boucle (qui, évidemment, devrait être déclarée aListIterator
plutôt que anIterator
).la source
e = iterator.next()
alors fairee = somethingElse
change simplement l'objete
auquel il se réfère plutôt que de changer le magasin réel à partir duqueliterator.next()
la valeur a été récupérée.e
ne changera pas le contenu de la liste; vous devez appelerlist.set(index, thing)
. Vous pouvez changer le contenu dee
(par exemple,e.setSomething(newValue)
), mais pour changer quel élément est stocké dans la liste pendant que vous l'itérez, vous devez vous en tenir à une itération basée sur un index.e
je dois appeler l'une dese
méthodes de, car l'affectation ne fait que modifier le pointeur (pardonnez mon C).Integer
etString
il ne serait pas possible de modifier le contenu à l'aide defor-each
ou desIterator
méthodes - il faudrait manipuler l'objet de liste lui-même pour le remplacer par des éléments. Est-ce correct?Exemple de chaque type énuméré dans la question:
ListIterationExample.java
la source
La boucle de base n'est pas recommandée car vous ne connaissez pas l'implémentation de la liste.
S'il s'agissait d'une LinkedList, chaque appel à
serait itérer sur la liste, entraînant une complexité temporelle de N ^ 2.
la source
Une itération de style JDK8:
la source
Dans Java 8, nous avons plusieurs façons d'itérer sur les classes de collection.
Utiliser Iterable forEach
Les collections qui implémentent
Iterable
(par exemple toutes les listes) ont désormais uneforEach
méthode. Nous pouvons utiliser la référence de méthode introduite dans Java 8.Utilisation de Streams forEach et forEachOrdered
Nous pouvons également parcourir une liste en utilisant Stream comme:
Nous devrions préférer
forEachOrdered
plutôtforEach
que le comportement deforEach
est explicitement non déterministe alors queforEachOrdered
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 sera conservée.L'avantage des flux est que nous pouvons également utiliser des flux parallèles le cas échéant. Si l'objectif est uniquement d'imprimer les articles quelle que soit la commande, nous pouvons utiliser le flux parallèle comme:
la source
Je ne sais pas ce que vous considérez comme pathologique, mais permettez-moi de vous proposer quelques alternatives que vous n'auriez pas pu voir auparavant:
Ou sa version récursive:
En outre, une version récursive du classique
for(int i=0...
:Je les mentionne car vous êtes "un peu nouveau pour Java" et cela pourrait être intéressant.
la source
while(!copyList.isEmpty()){ E e = copyList.remove(0); ... }
. C'est plus efficace que la première version;).Vous pouvez utiliser forEach à partir de Java 8:
la source
Pour une recherche en arrière, vous devez utiliser les éléments suivants:
Si vous voulez connaître une position, utilisez iterator.previousIndex (). Cela aide également à écrire une boucle interne qui compare deux positions dans la liste (les itérateurs ne sont pas égaux).
la source
À droite, de nombreuses alternatives sont répertoriées. Le plus simple et le plus propre serait simplement d'utiliser l'
for
instruction améliorée comme ci-dessous. LeExpression
est d'un type qui est itérable.Par exemple, pour parcourir les identifiants List <String>, nous pouvons simplement le faire,
la source
Dans,
java 8
vous pouvez utiliser laList.forEach()
méthode aveclambda expression
pour parcourir une liste.la source
eugene82
la réponse et dei_am_zero
la réponse ?Vous pouvez toujours changer les premier et troisième exemples avec une boucle while et un peu plus de code. Cela vous donne l'avantage de pouvoir utiliser le do-while:
Bien sûr, ce genre de chose peut provoquer une NullPointerException si list.size () renvoie 0, car 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. Pourtant, il est beaucoup plus simple et plus facile d'utiliser la boucle for
la source