AFAIK, il existe deux approches:
- Itérer sur une copie de la collection
- Utilisez l'itérateur de la collection actuelle
Par exemple,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
et
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
Y a-t-il des raisons de préférer une approche à l'autre (par exemple, préférer la première approche pour la simple raison de la lisibilité)?
java
collections
iteration
user1329572
la source
la source
while
eu différentes règles de portée quefor
fooList
est une variable d'instance et vous appelez une méthode pendant la boucle qui finit par appeler une autre méthode de la même classefooList.remove(obj)
. J'ai vu cela se produire. Dans ce cas, la copie de la liste est la plus sûre.Réponses:
Permettez-moi de donner quelques exemples avec quelques alternatives pour éviter a
ConcurrentModificationException
.Supposons que nous ayons la collection de livres suivante
Collecter et supprimer
La première technique consiste à collecter tous les objets que nous voulons supprimer (par exemple en utilisant une boucle for améliorée) et après avoir terminé l'itération, nous supprimons tous les objets trouvés.
Cela suppose que l'opération que vous souhaitez effectuer est "supprimer".
Si vous souhaitez "ajouter" cette approche fonctionnerait également, mais je suppose que vous parcourrez une autre collection pour déterminer les éléments que vous souhaitez ajouter à une deuxième collection, puis émettez une
addAll
méthode à la fin.Utilisation de ListIterator
Si vous travaillez avec des listes, une autre technique consiste à utiliser une
ListIterator
qui prend en charge la suppression et l'ajout d'éléments lors de l'itération elle-même.Encore une fois, j'ai utilisé la méthode "remove" dans l'exemple ci-dessus, ce que votre question semblait impliquer, mais vous pouvez également utiliser sa
add
méthode pour ajouter de nouveaux éléments pendant l'itération.Utilisation de JDK> = 8
Pour ceux qui travaillent avec Java 8 ou des versions supérieures, il existe quelques autres techniques que vous pouvez utiliser pour en tirer parti.
Vous pouvez utiliser la nouvelle
removeIf
méthode dans laCollection
classe de base:Ou utilisez la nouvelle API de flux:
Dans ce dernier cas, pour filtrer les éléments d'une collection, vous réaffectez la référence d'origine à la collection filtrée (c'est-à-dire
books = filtered
) ou vous avez utilisé la collection filtrée pourremoveAll
les éléments trouvés de la collection d'origine (c'est-à-direbooks.removeAll(filtered)
).Utiliser une sous-liste ou un sous-ensemble
Il existe également d'autres alternatives. Si la liste est triée et que vous souhaitez supprimer des éléments consécutifs, vous pouvez créer une sous-liste, puis la supprimer:
Étant donné que la sous-liste est appuyée par la liste d'origine, ce serait un moyen efficace de supprimer cette sous-collection d'éléments.
Quelque chose de similaire pourrait être réalisé avec des ensembles triés à l'aide de la
NavigableSet.subSet
méthode ou de l'une des méthodes de découpage proposées.Considérations:
La méthode que vous utilisez peut dépendre de ce que vous avez l'intention de faire
removeAl
technique fonctionnent avec n'importe quelle collection (collection, liste, ensemble, etc.).ListIterator
technique ne fonctionne évidemment qu'avec des listes, à condition que leurListIterator
implémentation donnée prenne en charge les opérations d'ajout et de suppression.Iterator
approche fonctionnerait avec n'importe quel type de collection, mais elle ne prend en charge que les opérations de suppression.ListIterator
/Iterator
, l'avantage évident est de ne pas avoir à copier quoi que ce soit puisque nous supprimons au fur et à mesure de l'itération. Donc, c'est très efficace.removeAll
approche, l'inconvénient est que nous devons répéter deux fois. D'abord, nous parcourons dans la boucle du plancher à la recherche d'un objet qui correspond à nos critères de suppression, et une fois que nous l'avons trouvé, nous demandons de le supprimer de la collection d'origine, ce qui impliquerait un deuxième travail d'itération pour rechercher cet article afin de le retirer.Iterator
interface est marquée comme "facultative" dans Javadocs, ce qui signifie qu'il pourrait y avoir desIterator
implémentations qui jettentUnsupportedOperationException
si nous invoquons la méthode remove. En tant que tel, je dirais que cette approche est moins sûre que d'autres si nous ne pouvons pas garantir le support de l'itérateur pour la suppression d'éléments.la source
removeAll(filtered)
. Un raccourci pour cela seraitremoveIf(b -> b.getIsbn().equals(other))
En Java 8, il existe une autre approche. Collection # removeIf
par exemple:
la source
La première approche fonctionnera, mais a la surcharge évidente de copier la liste.
La deuxième approche ne fonctionnera pas car de nombreux conteneurs ne permettent pas de modification pendant l'itération. Cela comprend
ArrayList
.Si la seule modification est de supprimer l'élément courant, vous pouvez faire le deuxième travail d'approche à l'aide
itr.remove()
(qui est, utilisez la iterator « sremove()
méthode, pas le contenant » s). Ce serait ma méthode préférée pour les itérateurs qui prennent en chargeremove()
.la source
Iterator
interface est marquée comme facultative dans Javadocs, ce qui signifie qu'il pourrait y avoir des implémentations d'itérateurUnsupportedOperationException
. En tant que tel, je dirais que cette approche est moins sûre que la première. En fonction des implémentations destinées à être utilisées, la première approche pourrait être plus adaptée.remove()
sur la collection originale elle-même peut également renvoyerUnsupportedOperationException
: docs.oracle.com/javase/7/docs/api/java/util/… . Les interfaces de conteneurs Java sont, malheureusement, définies comme extrêmement peu fiables (ce qui va à l'encontre du point de l'interface, honnêtement). Si vous ne connaissez pas l'implémentation exacte qui sera utilisée lors de l'exécution, il est préférable de faire les choses de manière immuable - par exemple, utilisez l'API Java 8+ Streams pour filtrer les éléments et les collecter dans un nouveau conteneur, puis remplacer entièrement l'ancien par lui.Seule la deuxième approche fonctionnera. Vous pouvez modifier la collection pendant l'itération en utilisant
iterator.remove()
uniquement. Toutes les autres tentatives entraînerontConcurrentModificationException
.la source
Old Timer Favorite (ça marche toujours):
la source
Vous ne pouvez pas faire la seconde, car même si vous utilisez la
remove()
méthode sur Iterator , vous obtiendrez une exception levée .Personnellement, je préférerais le premier pour toutes les
Collection
instances, malgré le surprenant supplémentaire de créer le nouveauCollection
, je le trouve moins sujet aux erreurs lors de l'édition par d'autres développeurs. Sur certaines implémentations de Collection, l'itérateurremove()
est pris en charge, sur d'autres il ne l'est pas. Vous pouvez en savoir plus dans les documents pour Iterator .La troisième alternative consiste à créer un nouveau
Collection
, itérer sur l'original et ajouter tous les membres du premierCollection
au secondCollection
qui ne sont pas prêts à être supprimés. En fonction de la tailleCollection
et du nombre de suppressions, cela pourrait considérablement économiser de la mémoire par rapport à la première approche.la source
Je choisirais la seconde car vous n'avez pas à faire de copie de la mémoire et l'itérateur fonctionne plus rapidement. Vous économisez ainsi de la mémoire et du temps.
la source
pourquoi pas ça?
Et si c'est une carte, pas une liste, vous pouvez utiliser keyset ()
la source
get(i)
vous devez visiter tous les nœuds jusqu'à ce que vous atteigniezi
.Foo.remove(i);
vous devriez le fairei--;
?