web-dev-qa-db-fra.com

Comment modifier une collection tout en itérant à l'aide de la boucle for-each sans ConcurrentModificationException?

Si je modifie une collection en itérant dessus en utilisant une boucle for-each, cela donne ConcurrentModificationException. Y a-t-il une solution de contournement?

22
aps

Utilisation Iterator#remove .

C'est le seul moyen sûr de modifier une collection pendant l'itération. Pour plus d'informations, voir The Collection Interface tutorial.

Si vous avez également besoin de la capacité d'ajouter des éléments pendant l'itération, utilisez un ListIterator .

39
mre

Une solution consiste à enregistrer vos modifications et à les ajouter/supprimer après la boucle.

Par exemple:

List<Item> toRemove = new LinkedList<Item>();

for(Item it:items){
    if(remove){
        toRemove.add(it);
    }
}
items.removeAll(toRemove);
11
jzd

Une deuxième solution consiste à utiliser une classe de collection dont les itérateurs ne donneront pas l'exception. Par exemple ConcurrentLinkedQueue , ConcurrentHashMap et ainsi de suite.

Ceux-ci évitent d'avoir à lever des exceptions en fournissant des modèles de cohérence plus faibles pour les itérateurs. (Bien sûr, vous devez comprendre ces modèles et décider s'ils conviennent à votre application.)

Ils sont généralement un peu plus lents que les collections non simultanées, mais plus rapides que les encapsuleurs de collection synchronisés en cas de conflit important.

3
Stephen C

Si vous souhaitez simplement supprimer l'élément de la collection, vous pouvez utiliser Iterator au lieu d'Iterable.

Sinon, ce que vous pouvez faire n'est pas d'itérer la collection d'origine, mais faites d'abord une copie de la liste. Par exemple, si votre collection est une liste, vous pouvez créer une nouvelle ArrayList (originaList) et itérer dessus. La modification doit être effectuée sur la liste d'origine.

Une autre alternative, peut-être meilleure pour votre cas d'utilisation, n'est pas d'utiliser pour chacun mais le traditionnel pour.

1
nanda