Nous savons tous que vous ne pouvez pas faire ce qui suit à cause de ConcurrentModificationException
:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
Mais cela fonctionne apparemment parfois, mais pas toujours. Voici un code spécifique:
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
l.add(4);
l.add(5);
l.add(6);
}
for (int i : l) {
if (i == 5) {
l.remove(i);
}
}
System.out.println(l);
}
Cela se traduit bien sûr par:
Exception in thread "main" java.util.ConcurrentModificationException
Même si plusieurs threads ne le font pas. En tous cas.
Quelle est la meilleure solution à ce problème? Comment puis-je supprimer un élément de la collection dans une boucle sans lever cette exception?
J'utilise également un arbitraire Collection
ici, pas nécessairement un ArrayList
, donc vous ne pouvez pas vous fier get
.
java
collections
iteration
Claudiu
la source
la source
Réponses:
Iterator.remove()
est sûr, vous pouvez l'utiliser comme ceci:Notez que
Iterator.remove()
c'est le seul moyen sûr de modifier une collection pendant l'itération; le comportement n'est pas spécifié si la collection sous-jacente est modifiée d'une autre manière pendant l'itération.Source: docs.oracle> L'interface de collection
Et de même, si vous en avez un
ListIterator
et que vous souhaitez ajouter des éléments, vous pouvez utiliserListIterator#add
, pour la même raison que vous pouvez utiliserIterator#remove
- il est conçu pour le permettre.Dans votre cas, vous avez tenté de supprimer une liste, mais la même restriction s'applique si vous essayez de le faire
put
enMap
itérant son contenu.la source
iterator.next()
appel dans la boucle for? Sinon, quelqu'un peut-il expliquer pourquoi?List.add
est "facultatif" dans le même sens aussi, mais vous ne diriez pas qu'il est "dangereux" d'ajouter à une liste.Cela marche:
J'ai supposé que, comme une boucle foreach est un sucre syntaxique pour l'itération, l'utilisation d'un itérateur n'aiderait pas ... mais cela vous donne cette
.remove()
fonctionnalité.la source
Avec Java 8, vous pouvez utiliser la nouvelle
removeIf
méthode . Appliqué à votre exemple:la source
removeIf
utiliseIterator
etwhile
boucle. Vous pouvez le voir au java 8java.util.Collection.java
ArrayList
remplacer pour des raisons de performances. Celui dont vous parlez n'est que l'implémentation par défaut.equals
n'est pas utilisé du tout ici, donc il n'a pas besoin d'être implémenté. (Mais bien sûr, si vous utilisezequals
dans votre test, il doit être implémenté comme vous le souhaitez.)Puisque la question a déjà été répondue, c'est-à-dire que la meilleure façon est d'utiliser la méthode remove de l'objet itérateur, j'entrerais dans les détails de l'endroit où l'erreur
"java.util.ConcurrentModificationException"
est levée.Chaque classe de collection a une classe privée qui implémente l'interface Iterator et fournit des méthodes comme
next()
,remove()
ethasNext()
.Le code pour next ressemble à ceci ...
Ici, la méthode
checkForComodification
est implémentée commeDonc, comme vous pouvez le voir, si vous essayez explicitement de supprimer un élément de la collection. Il en résulte
modCount
une différence avecexpectedModCount
, ce qui entraîne l'exceptionConcurrentModificationException
.la source
Vous pouvez soit utiliser l'itérateur directement comme vous l'avez mentionné, soit conserver une deuxième collection et ajouter chaque élément que vous souhaitez supprimer à la nouvelle collection, puis supprimer tout à la fin. Cela vous permet de continuer à utiliser la sécurité de type de la boucle for-each au prix d'une utilisation accrue de la mémoire et du temps processeur (cela ne devrait pas être un gros problème sauf si vous avez de très, très grandes listes ou un très vieil ordinateur)
la source
Dans de tels cas, une astuce courante est (était?) De revenir en arrière:
Cela dit, je suis plus qu'heureux que vous ayez de meilleures méthodes en Java 8, par exemple
removeIf
oufilter
sur les flux.la source
ArrayList
les collections s ou similaires.for(int i = l.size(); i-->0;) {
?Même réponse que Claudius avec une boucle for:
la source
Avec les collections Eclipse , la méthode
removeIf
définie sur MutableCollection fonctionnera:Avec la syntaxe Java 8 Lambda, cela peut être écrit comme suit:
L'appel à
Predicates.cast()
est nécessaire ici car uneremoveIf
méthode par défaut a été ajoutée sur l'java.util.Collection
interface dans Java 8.Remarque: je suis un committer pour les collections Eclipse .
la source
Faites une copie de la liste existante et parcourez la nouvelle copie.
la source
Les gens affirment que l' on ne peut pas supprimer d'une collection en cours d'itération par une boucle foreach. Je voulais juste souligner que c'est techniquement incorrect et décrire exactement (je sais que la question de l'OP est si avancée qu'elle évite de le savoir) le code derrière cette hypothèse:
Ce n'est pas que vous ne pouvez pas supprimer de l'itéré
Colletion
plutôt que vous ne pouvez pas continuer l'itération une fois que vous l'avez fait. D'où lebreak
dans le code ci-dessus.Toutes mes excuses si cette réponse est un cas d'utilisation quelque peu spécialisé et plus adapté au fil d' origine d'où je suis arrivé ici, celui-ci est marqué comme doublon (malgré ce fil apparaissant plus nuancé) de celui-ci et verrouillé.
la source
Avec une boucle for traditionnelle
la source
i++
dans le bouclier plutôt que dans le corps de la boucle.i++
incrémentation n'était pas conditionnelle - je vois maintenant c'est pourquoi vous le faites dans le corps :)A
ListIterator
vous permet d'ajouter ou de supprimer des éléments dans la liste. Supposons que vous ayez une liste d'Car
objets:la source
previous
méthode.J'ai une suggestion pour le problème ci-dessus. Pas besoin de liste secondaire ni de temps supplémentaire. Veuillez trouver un exemple qui ferait la même chose mais d'une manière différente.
Cela éviterait l'exception de concurrence.
la source
ArrayList
et ne peut donc pas compterget()
. Sinon, c'est probablement une bonne approche.Collection
- l'Collection
interface ne comprend pasget
. (Bien que l'List
interface FWIW inclue 'get').while
bouclage aList
. Mais +1 pour cette réponse car elle est venue en premier.ConcurrentHashMap ou ConcurrentLinkedQueue ou ConcurrentSkipListMap peuvent être une autre option, car ils ne lèveront jamais d'exception ConcurrentModificationException, même si vous supprimez ou ajoutez un élément.
la source
java.util.concurrent
paquet. Certaines autres classes de cas d'utilisation similaires / communes de ce package sontCopyOnWriteArrayList
&CopyOnWriteArraySet
[mais sans s'y limiter].ConcurrentModificationException
, leur utilisation dans une boucle améliorée peut toujours causer des problèmes d'indexation (c.-à-d. Toujours sauter des éléments, ouIndexOutOfBoundsException
...)Je sais que cette question est trop ancienne pour être sur Java 8, mais pour ceux qui utilisent Java 8, vous pouvez facilement utiliser removeIf ():
la source
Une autre façon consiste à créer une copie de votre arrayList:
la source
i
n'est pas unindex
mais plutôt l'objet. Peut-être que l'appelerobj
serait plus approprié.la source
Dans le cas où ArrayList: remove (int index) - si (index est la position du dernier élément), il évite sans
System.arraycopy()
et ne prend pas de temps pour cela.le temps de tableau augmente si (l'index diminue), d'ailleurs les éléments de la liste diminuent aussi!
le meilleur moyen de suppression efficace est de supprimer ses éléments dans l'ordre décroissant:
while(list.size()>0)list.remove(list.size()-1);
// prend O (1)while(list.size()>0)list.remove(0);
// prend O (factoriel (n))la source
La capture est après avoir supprimé l'élément de la liste si vous ignorez l'appel interne iterator.next (). ça fonctionne encore! Bien que je ne propose pas d'écrire du code comme celui-ci, cela aide à comprendre le concept derrière :-)
À votre santé!
la source
Exemple de modification de collection thread-safe:
la source
Je sais que cette question suppose juste un
Collection
, et pas plus spécifiquement aucunList
. Mais pour ceux qui lisent cette question qui travaillent en effet avec uneList
référence, vous pouvez éviterConcurrentModificationException
avec unwhile
-loop (tout en le modifiant) à la place si vous voulez éviterIterator
(soit si vous voulez l'éviter en général, soit l'éviter spécifiquement pour atteindre un ordre de bouclage différent de l'arrêt de bout en bout à chaque élément [ce qui, à mon avis, est le seul ordre enIterator
soi]]:* Mise à jour: Voir les commentaires ci-dessous qui précisent que l'analogue est également réalisable avec la boucle traditionnelle .
Aucune exception ConcurrentModificationException de ce code.
Là, nous voyons que le bouclage ne démarre pas au début et ne s'arrête pas à chaque élément (ce que je crois
Iterator
lui-même ne peut pas faire).FWIW, nous voyons également
get
être appelélist
, ce qui ne pourrait pas être fait si sa référence était justeCollection
(au lieu duList
type plus spécifique deCollection
) - l'List
interface inclutget
, mais pas l'Collection
interface. Si ce n'est pas pour cette différence, alors lalist
référence pourrait plutôt être unCollection
[et donc techniquement cette réponse serait alors une réponse directe, au lieu d'une réponse tangentielle].FWIWW même code fonctionne toujours après modification pour commencer au début à l'arrêt à chaque élément (comme l'
Iterator
ordre):la source
ConcurrentModificationException
, mais pas la boucle traditionnelle (que l'autre réponse utilise) - ne réalisant pas qu'avant, c'est pourquoi j'étais motivé à écrire cette réponse (j'ai erronément pensait alors que toutes les boucles for allaient lever l'exception).Une solution pourrait être de faire pivoter la liste et de supprimer le premier élément pour éviter la ConcurrentModificationException ou IndexOutOfBoundsException
la source
Essayez celui-ci (supprime tous les éléments de la liste qui sont égaux
i
):la source
vous pouvez également utiliser la récursivité
La récursivité en java est un processus dans lequel une méthode s'appelle elle-même en continu. Une méthode en Java qui s'appelle elle-même est appelée méthode récursive.
la source
ce n'est peut-être pas le meilleur moyen, mais pour la plupart des petits cas, cela devrait être acceptable:
Je ne me souviens pas d'où je lis ceci ... par justesse, je ferai ce wiki dans l'espoir que quelqu'un le trouve ou simplement pour ne pas gagner de réputation que je ne mérite pas.
la source