Appeler remove dans la boucle foreach en Java [dupliquer]


Cette question a déjà une réponse ici:

En Java, est-il légal d'appeler remove sur une collection lors de l'itération dans la collection à l'aide d'une boucle foreach? Par exemple:

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

En tant qu'addendum, est-il légal de supprimer des éléments qui n'ont pas été itéré encore? Par exemple,

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}
Author: James McMahon, 2009-07-29

11 answers

Pour supprimer en toute sécurité d'une collection tout en itérant dessus, vous devez utiliser un itérateur.

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 renvoyés par l'itérateur et le listIterator de cette classe les méthodes sont rapides: si la liste est structurellement modifiée à tout moment temps après la création de l'itérateur, de quelque manière que ce soit, sauf par propres méthodes supprimer ou ajouter de l'itérateur, l'itérateur lancera un ConcurrentModificationException. Ainsi, face à des concurrentes modification, l'itérateur échoue rapidement et proprement, plutôt que risquer un comportement arbitraire et non déterministe à un moment indéterminé dans l'avenir.

Ce qui n'est peut-être pas clair pour de nombreux novices, c'est le fait que l'itération sur une liste en utilisant les constructions for/foreach crée implicitement un itérateur nécessairement inaccessible. Cette information peut être trouvée ici

 792
Author: Mark, 2014-12-02 11:11:03

Vous ne voulez pas faire ça. Cela peut provoquer un comportement indéfini en fonction de la collection. Vous voulez utiliser unItérateur directement. Bien que pour chaque construction soit du sucre syntaxique et utilise vraiment un itérateur, il le cache de votre code afin que vous ne puissiez pas y accéder pour appeler Iterator.remove.

Le comportement d'un itérateur est non spécifié si le sous-jacent la collection est modifiée alors que le l'itération est en cours de quelque manière que ce soit d'autres que par l'appel de cette méthode.

Écrivez plutôt 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, pas List.remove.

Additif:

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

 152
Author: Jared Oberhaus, 2009-07-28 20:48:37

La conception java de la "enhanced for loop" é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 pouvez ajouter les éléments à une collection temporaire et appeler removeAll sur la liste après la boucle.

EDIT (re addendum): Non, changer la liste de quelque manière que ce soit en dehors de l'itérateur.méthode remove() alors que l'itération causera des problèmes. Le seul moyen de contourner cela est d'utiliser une CopyOnWriteArrayList, mais qui est vraiment destiné aux problèmes de concurrence.

Le moyen le moins cher (en termes de lignes de code) de supprimer les doublons est de vider la liste dans un LinkedHashSet (puis de nouveau dans une liste si vous en avez besoin). Cela préserve l'ordre d'insertion tout en supprimant les doublons.

 55
Author: Yishai, 2014-07-18 13:31:40
for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

Vous clonez la liste names et parcourez le clone pendant que vous supprimez de la liste d'origine. Un peu plus propre que la réponse supérieure.

 44
Author: ktamlyn, 2015-11-22 03:37:30

Je ne connaissais pas les itérateurs, mais voici ce que je faisais jusqu'à aujourd'hui pour supprimer des éléments d'une liste à l'intérieur d'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.

 24
Author: Serafeim, 2013-02-27 14:53:42

Oui, vous pouvez utiliser la boucle for-each, Pour ce faire, vous devez maintenir une liste distincte pour contenir la suppression d'éléments, puis supprimer cette liste de la liste des noms en utilisant la méthode removeAll(),

List<String> names = ....

// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();

for (String name : names) {
   // Do something: perform conditional checks
   toRemove.add(name);
}    
names.removeAll(toRemove);

// now names list holds expected values
 20
Author: Chathuranga Withana, 2010-12-11 13:00:54

Ceux qui disent que vous ne pouvez pas supprimer en toute sécurité un élément d'une collection sauf via l'itérateur ne sont pas tout à fait corrects, vous pouvez le faire en toute sécurité en utilisant l'une des collections concurrentes telles que ConcurrentHashMap.

 3
Author: sanity, 2009-07-28 23:25:51

Assurez-vous que ce n'est pas une odeur de code. Est-il possible d'inverser la logique et être "inclusif", plutôt que "d'exclusif"?

List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
   // Do something
   if (conditionToIncludeMet)
       reducedNames.add(name);
}
return reducedNames;

La situation qui m'a conduit à cette page impliquait un ancien code qui parcourait une liste en utilisant des indécies pour supprimer des éléments de la liste. Je voulais le refactoriser pour utiliser le style foreach.

Il a parcouru une liste complète d'éléments pour vérifier ceux auxquels l'utilisateur avait l'autorisation d'accéder, et a supprimé ceux qui n'avaient pas l'autorisation du liste.

List<Service> services = ...
for (int i=0; i<services.size(); i++) {
    if (!isServicePermitted(user, services.get(i)))
         services.remove(i);
}

Pour inverser cela et ne pas utiliser le remove:

List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
    if (isServicePermitted(user, service))
         permittedServices.add(service);
}
return permittedServices;

Quand "supprimer" serait-il préféré? Une considération est si gien une grande liste ou cher "ajouter", combiné avec seulement quelques supprimé par rapport à la taille de la liste. Il pourrait être plus efficace de ne faire que quelques suppressions plutôt qu'un grand nombre d'ajouts. Mais dans mon cas, la situation ne méritait pas une telle optimisation.

 3
Author: bmcdonald, 2011-11-14 16:57:57
  1. Essayez ceci 2. et changez la condition en "HIVER" et vous vous demanderez:
public static void main(String[] args) {
  Season.add("Frühling");
  Season.add("Sommer");
  Season.add("Herbst");
  Season.add("WINTER");
  for (String s : Season) {
   if(!s.equals("Sommer")) {
    System.out.println(s);
    continue;
   }
   Season.remove("Frühling");
  }
 }
 1
Author: Carsten, 2011-08-30 15:42:18

, Il est préférable d'utiliser un Itérateur lorsque vous souhaitez supprimer l'élément de la liste

Parce que le code source de remove est

if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
elementData[--size] = null;

Donc ,si vous supprimez un élément de la liste, la liste sera restructurée ,l'index de l'autre élément sera modifié, cela peut entraîner quelque chose que vous voulez arriver.

 1
Author: song, 2012-02-09 08:51:44

Utiliser

.supprimer () d'Interator ou

Utiliser

CopyOnWriteArrayList

 -5
Author: ThmHarsh, 2014-10-10 05:41:46