Appel supprimer dans la boucle foreach en Java [dupliquer]

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

en Java, est-il légal d'appeler supprimer sur une collection en itérant à travers la collection en utilisant une boucle foreach? Par exemple:

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

comme addendum, est-il légal de supprimer des éléments qui n'ont pas encore été répétés? Par exemple,

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}
525
demandé sur James McMahon 2009-07-29 00:39:07
la source

11 ответов

pour retirer 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 de Java :

les itérateurs retournés par l'itérateur et le listiterateur de cette classe les méthodes sont fail-fast: si la liste est structurellement modifiés à tout temps après la création de l'itérateur, de quelque manière que ce soit, sauf itérateur de l' propre supprimer ou ajouter des méthodes, l'itérateur lancera un ConcurrentModificationException. Ainsi, face à des concurrentes la modification, l'itérateur échoue rapidement et proprement, plutôt que de risque de comportement arbitraire et non déterministe à un moment indéterminé dans l'avenir.

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

792
répondu Mark 2014-12-02 14:11:03
la source

tu ne veux pas faire ça. Il peut causer un comportement non défini en fonction de la collection. Vous voulez utiliser un Iterator directement. Bien que le pour chaque construction est du sucre syntaxique et utilise vraiment un itérateur, il le cache de votre code de sorte que vous ne pouvez pas y accéder pour appeler Iterator.remove .

Le comportement d'un itérateur est non spécifié si le sous-jacent la collecte est modifiée pendant que le l'itération en cours, en quelque sorte d'autres que par l'appel de cette méthode.

à la place é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 et non List.remove .

Addendum:

même si vous supprimez un élément qui n'a pas encore été itéré, vous ne voulez pas modifier la collection et ensuite utiliser le Iterator . Il pourrait modifier la collection d'une manière qui surprend et affecte les opérations futures sur le Iterator .

152
répondu Jared Oberhaus 2009-07-29 00:48:37
la source

la conception java du" 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 enhanced for vaut vraiment la peine, alors vous pouvez ajouter les éléments à une collection temporaire et appeler removeAll sur la liste après la boucle.

MODIFIER (re addendum): Non, la modification de la liste en quelque sorte en dehors de l'itérateur.méthode remove() lors de l'itération va causer des problèmes. La seule façon d'éviter cela est d'utiliser un CopyOnWriteArrayList, mais qui est vraiment destiné à des questions de concurrence.

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

55
répondu Yishai 2014-07-18 17:31:40
la source
for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

vous clonez la liste names et itérez à travers le clone pendant que vous retirez de la liste originale. Un peu plus propre que la réponse sommet.

44
répondu ktamlyn 2015-11-22 06:37:30
la source

Je ne savais pas à propos des 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 langues ou structures ne supportant pas les itérateurs.

24
répondu Serafeim 2013-02-27 18:53:42
la source

Oui vous pouvez utiliser la boucle pour chaque, Pour ce faire, vous devez maintenir une liste séparée pour tenir supprimer des articles et puis supprimer cette liste de la liste des noms en utilisant removeAll() méthode,

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
répondu Chathuranga Withana 2010-12-11 16:00:54
la source

ceux qui disent que vous ne pouvez pas retirer un article d'une collection en toute sécurité sauf par 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 Competienthashmap.

3
répondu sanity 2009-07-29 03:25:51
la source

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 vieux code qui a fait un tour dans une liste en utilisant des indecies pour supprimer des éléments de la liste. Je voulais le reformuler pour qu'il utilise le style foreach.

il a passé en boucle une liste complète d'éléments pour vérifier lesquels l'utilisateur avait la permission d'accéder, et a supprimé ceux qui n'ont pas la permission de la liste.

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

pour inverser cette ET NE PAS utiliser le supprimer:

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érable? Une considération est si gien une grande liste ou cher "ajouter", combiné avec seulement quelques enlevés 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'additions. Mais dans mon cas, la situation ne mérite pas une telle optimisation.

3
répondu bmcdonald 2011-11-14 20:57:57
la source
  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
répondu Carsten 2011-08-30 19:42:18
la source

il est préférable d'utiliser un itérateur lorsque vous voulez supprimer un élément d'une 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 restructurer ,l'autre de l'élément d'index sera modifié, il peut en résulter quelque chose que vous voulez est arrivé.

1
répondu song 2012-02-09 12:51:44
la source

Utiliser

.Supprimer () de L'Interator ou

Utiliser

CopyOnWriteArrayList

-5
répondu ThmHarsh 2014-10-10 09:41:46
la source

Autres questions sur java iterator loops foreach