Éviter l'exception ConcurrentModificationException en Java

1. Introduction

Dans cet article, nous examinerons la classe ConcurrentModificationException .

Tout d'abord, nous expliquerons comment cela fonctionne, puis nous le prouverons en utilisant un test pour le déclencher.

Enfin, nous allons essayer quelques solutions de contournement en utilisant des exemples pratiques.

2. Déclenchement d'une exception ConcurrentModificationException

Essentiellement, l' exception ConcurrentModificationException est utilisée pour échouer rapidement lorsque quelque chose sur lequel nous itérons est modifié. Prouvons cela avec un simple test:

@Test(expected = ConcurrentModificationException.class) public void whilstRemovingDuringIteration_shouldThrowException() throws InterruptedException { List integers = newArrayList(1, 2, 3); for (Integer integer : integers) { integers.remove(1); } }

Comme nous pouvons le voir, avant de terminer notre itération, nous supprimons un élément. C'est ce qui déclenche l'exception.

3. Solutions

Parfois, nous pouvons en fait vouloir supprimer des éléments d'une collection lors de l'itération. Si tel est le cas, il existe des solutions.

3.1. Utilisation directe d'un itérateur

Une boucle for-each utilise un itérateur dans les coulisses mais est moins verbeuse. Cependant, si nous avons remanié notre test précédent pour utiliser un Iterator, nous aurons accès à des méthodes supplémentaires, telles que remove (). Essayons d'utiliser cette méthode pour modifier notre liste à la place:

for (Iterator iterator = integers.iterator(); iterator.hasNext();) { Integer integer = iterator.next(); if(integer == 2) { iterator.remove(); } }

Maintenant, nous remarquerons qu'il n'y a pas d'exception. La raison en est que la méthode remove () ne provoque pas d'exception ConcurrentModificationException. Il est prudent d'appeler pendant l'itération.

3.2. Ne pas supprimer pendant l'itération

Si nous voulons garder notre boucle for-each , alors nous pouvons. C'est juste que nous devons attendre après l'itération avant de supprimer les éléments. Essayons ceci en ajoutant ce que nous voulons supprimer à une liste toRemove au fur et à mesure que nous itérons:

List integers = newArrayList(1, 2, 3); List toRemove = newArrayList(); for (Integer integer : integers) { if(integer == 2) { toRemove.add(integer); } } integers.removeAll(toRemove); assertThat(integers).containsExactly(1, 3); 

C'est un autre moyen efficace de contourner le problème.

3.3. Utilisation de removeIf ()

Java 8 a introduit la méthode removeIf () dans l' interface Collection . Cela signifie que si nous travaillons avec, nous pouvons utiliser des idées de programmation fonctionnelle pour obtenir à nouveau les mêmes résultats:

List integers = newArrayList(1, 2, 3); integers.removeIf(i -> i == 2); assertThat(integers).containsExactly(1, 3);

Ce style déclaratif nous offre le moins de verbosité. Cependant, selon le cas d'utilisation, nous pouvons trouver d'autres méthodes plus pratiques.

3.4. Filtrage à l'aide de flux

En plongeant dans le monde de la programmation fonctionnelle / déclarative, nous pouvons oublier la mutation des collections, nous pouvons plutôt nous concentrer sur les éléments qui devraient être réellement traités:

Collection integers = newArrayList(1, 2, 3); List collected = integers .stream() .filter(i -> i != 2) .map(Object::toString) .collect(toList()); assertThat(collected).containsExactly("1", "3");

Nous avons fait l'inverse de notre exemple précédent, en fournissant un prédicat pour déterminer les éléments à inclure et non à exclure. L'avantage est que nous pouvons enchaîner d'autres fonctions parallèlement à la suppression. Dans l'exemple, nous utilisons une carte fonctionnelle (), mais nous pourrions utiliser encore plus d'opérations si nous le souhaitons.

4. Conclusion

Dans cet article, nous avons montré les problèmes que vous pouvez rencontrer si vous supprimez des éléments d'une collection pendant l'itération, et avons également fourni des solutions pour annuler le problème.

L'implémentation de ces exemples est disponible à l'adresse over sur GitHub. Il s'agit d'un projet Maven, il devrait donc être facile à exécuter tel quel.