Suppression d'éléments d'une collection en Java : comment éviter ConcurrentModificationException

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Je veux supprimer un élément d'une liste dans une boucle — et j'obtiens ConcurrentModificationException ». Ça vous dit quelque chose ?

\n

Si vous avez déjà programmé en Java et tenté de supprimer un élément d'un ArrayList ou d'un HashMap pendant une itération (par exemple, dans une boucle for-each), vous avez sûrement vu cette terrible exception rouge. Elle surgit au pire moment, comme si Java vous disait : « Hé, qu'est-ce que tu fabriques ? N'ose pas toucher à la collection pendant que je la parcours ! ».

\n\n

Sur Stack Overflow, cette question a rassemblé des milliers de vues. Et ce n'est pas un hasard : le problème semble simple, mais sous le capot se cache tout un ensemble d'optimisations et de subtilités liées au multithreading. Dans cet article, nous allons voir comment supprimer correctement des objets d'une collection dans une boucle en Java, sans déclencher ConcurrentModificationException, tout en préservant les performances.

\n\n

C'est parti !

\n\n

Pourquoi ConcurrentModificationException se produit-elle ?

\n

Réponse courte : les itérateurs fail-fast. La plupart des collections du Java Collections Framework (ArrayList, HashSet, HashMap, etc.) utilisent un compteur interne de modifications (modCount). Lorsque vous créez un itérateur, il mémorise la valeur actuelle de modCount. À chaque appel de next(), l'itérateur vérifie si la collection a été modifiée de l'extérieur (via les méthodes add() ou remove() de la collection elle-même). Si le compteur diffère, l'exception est levée.

\n\n

Exemple de code qui échouera inévitablement :

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nfor (String item : list) {\n    if ("B".equals(item)) {\n        list.remove(item); // ConcurrentModificationException !\n    }\n}\n
\n

Pourquoi ? Parce que for-each utilise implicitement un itérateur, tandis que list.remove() modifie modCount directement, sans passer par l'itérateur. L'itérateur ne le pardonne pas.

\n\n

Méthode n°1 : Utiliser Iterator.remove() — la classique

\n

La manière la plus simple et la plus correcte est de prendre explicitement l'itérateur en main et d'appeler sa méthode remove(). Cette méthode synchronise le compteur interne de l'itérateur avec la collection.

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n    String item = iterator.next();\n    if ("B".equals(item)) {\n        iterator.remove(); // Sécurisé !\n    }\n}\nSystem.out.println(list); // [A, C, D]\n
\n

Avantages : sécurisé, fonctionne pour toutes les collections supportant les itérateurs. Inconvénients : un peu verbeux. Pour des filtrages simples, on peut utiliser des approches plus modernes.

\n\n

Méthode n°2 : Collection.removeIf() — moderne et élégant

\n

Depuis Java 8, la plupart des collections disposent de la méthode removeIf(Predicate filter). C'est la manière la plus concise de supprimer des éléments selon une condition.

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nlist.removeIf(item -> "B".equals(item));\nSystem.out.println(list); // [A, C, D]\n
\n

En interne, removeIf utilise un itérateur (ou un parcours direct avec conservation des indices), donc aucun ConcurrentModificationException ne se produira. C'est la méthode recommandée pour les cas simples.

\n

Performances : Pour ArrayList, la méthode removeIf s'exécute en O(n), mais elle le fait efficacement — elle décale les éléments en un seul passage, plutôt que de les supprimer un par un (ce qui serait en O(n²)).

\n\n

Méthode n°3 : Parcours inversé par index (pour List)

\n

Si, pour une raison quelconque, vous ne pouvez pas utiliser d'itérateur (par exemple, si vous devez modifier plusieurs éléments simultanément ou si vous travaillez avec du code ancien), vous pouvez parcourir la liste en sens inverse en utilisant une boucle for classique avec un index.

\n
List<String> list = new A                

Blogs

Book Recommendations