590 votes

Appeler remove dans une boucle foreach en Java

En Java, est-il légal d'appeler remove sur une collection lorsque l'on itère dans la collection à l'aide d'une boucle foreach ? Par exemple :

List<String> names = ....
for (String name : names) {
   // Do something
   names.remove(name).
}

Par ailleurs, est-il légal de supprimer des éléments qui n'ont pas encore fait l'objet d'une itération ? Par exemple,

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}

897voto

Mark Points 14208

Pour effectuer des retraits en toute sécurité dans une collection tout en la parcourant par itération, vous devez utiliser un Iterator.

Par exemple :

List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
   String s = i.next(); // must be called before you can call i.remove()
   // Do something
   i.remove();
}

De la Documentation Java :

Les itérateurs retournés par les itérateurs de cette classe et les listIterator. sont infaillibles : si la liste est structurellement modifiée à n'importe quel après la création de l'itérateur, de quelque manière que ce soit, sauf par de l'itérateur, l'itérateur déclenchera une erreur de type ConcurrentModificationException. Ainsi, en cas de modification simultanée concurrentes, l'itérateur échoue rapidement et proprement, plutôt que de plutôt que de risquer un comportement arbitraire et non déterministe à un moment indéterminé dans le futur.

Ce qui n'est peut-être pas clair pour beaucoup de novices, c'est le fait que l'itération sur une liste à l'aide des constructions for/foreach crée implicitement un itérateur qui est nécessairement inaccessible. Cette information peut être trouvée aquí

163voto

Jared Oberhaus Points 8877

Tu ne veux pas faire ça. Cela peut provoquer un comportement indéfini selon la collection. Vous voulez utiliser un Itérateur directement. Bien que la construction for each soit un sucre syntaxique et qu'elle utilise réellement un itérateur, elle le cache de votre code et vous ne pouvez pas y accéder pour appeler [Iterator.remove](http://java.sun.com/javase/6/docs/api/java/util/Iterator.html#remove()) .

Le comportement d'un itérateur est non spécifié si le sous-jacente est modifiée pendant que l'itération l'itération est en cours d'une autre manière autre que par l'appel de cette méthode.

Au lieu de cela, écrivez votre code :

List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {

    String name = it.next();
    // Do something
    it.remove();
}

Notez que le code appelle Iterator.remove no List.remove .

Addendum :

Même si vous supprimez un élément qui n'a pas encore été itéré, vous ne voulez toujours pas modifier la collection et utiliser ensuite la fonction Iterator . Il peut modifier la collection d'une manière surprenante et affecter les opérations futures sur la collection. Iterator .

63voto

ktamlyn Points 781
for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

Vous clonez la liste names et itérer dans le clone pendant que vous supprimez de la liste originale. Un peu plus propre que la première réponse.

61voto

Yishai Points 42417

La conception Java de la "boucle for améliorée" était de ne pas exposer l'itérateur au code, mais la seule façon de supprimer un élément en toute sécurité est d'accéder à l'itérateur. Donc, dans ce cas, vous devez le faire à l'ancienne :

 for(Iterator<String> i = names.iterator(); i.hasNext();) {
       String name = i.next();
       //Do Something
       i.remove();
 }

Si, dans le code réel, la boucle for améliorée en vaut vraiment la peine, vous pourriez ajouter les éléments à une collection temporaire et appeler removeAll sur la liste après la boucle.

EDIT (re addendum) : Non, modifier la liste de quelque manière que ce soit en dehors de la méthode iterator.remove() pendant l'itération posera des problèmes. La seule façon de contourner ce problème est d'utiliser une CopyOnWriteArrayList, mais cela est vraiment destiné aux problèmes de concurrence.

La méthode la plus économique (en termes de lignes de code) pour supprimer les doublons consiste à vider la liste dans un LinkedHashSet (puis à la retransformer en liste si nécessaire). Cela préserve l'ordre d'insertion tout en supprimant les doublons.

29voto

Serafeim Points 3812

Je ne connaissais pas les itérateurs, mais voici ce que je faisais jusqu'à aujourd'hui pour retirer des éléments d'une liste dans une boucle :

List<String> names = .... 
for (i=names.size()-1;i>=0;i--) {    
    // Do something    
    names.remove(i);
} 

Cela fonctionne toujours, et pourrait être utilisé dans d'autres langages ou structures ne supportant pas les itérateurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X