CodeGym /Cours /JAVA 25 SELF /Suppression sécurisée des éléments

Suppression sécurisée des éléments

JAVA 25 SELF
Niveau 28 , Leçon 2
Disponible

1. Le problème ConcurrentModificationException

Dans cette leçon, il n’y aura (presque) rien de nouveau. Mais elle est très importante, car la suppression imprudente de données fait partie des erreurs les plus irréparables — surtout en production. Et il y aura quand même une classe intéressante que vous allez probablement adorer !

Donc oui, encore et encore : pas de for-each pour supprimer ! Aussi tentant soit-il.

Commençons par un exemple classique qui cause, chez de nombreux débutants (et pas seulement), de la douleur et de la frustration :

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));

// Essayons de supprimer tous les nombres pairs
for (Integer n : numbers) {
    if (n % 2 == 0) {
        numbers.remove(n); // BOUM ! ConcurrentModificationException
    }
}

On dirait que tout devrait fonctionner, mais en réalité le programme lève une exception :

Exception in thread "main" java.util.ConcurrentModificationException

Voyons maintenant plus en détail ce qui s’est passé. Lorsque vous parcourez une collection avec for-each (ou un Iterator classique), la collection maintient en interne un « compteur de modifications ». Si, pendant le parcours, la collection est modifiée sans passer par l’itérateur lui-même, ce compteur détecte une « intervention extérieure » et lève une exception. C’est une protection contre les erreurs, afin d’éviter de travailler avec une structure de données corrompue.

2. Utiliser Iterator

Comment supprimer correctement des éléments pendant le parcours ?

Rappel : Iterator est un objet spécial qui permet de parcourir une collection et de supprimer des éléments en toute sécurité « à la volée ». C’est comme un serveur qui non seulement apporte les plats, mais peut aussi enlever une assiette pendant qu’il fait le tour de la table.

Obtention de l’itérateur

Iterator<Integer> it = numbers.iterator();

Parcours avec while et suppression via it.remove()

Voici la manière correcte de supprimer tous les nombres pairs de la liste :

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));

Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer n = it.next();
    if (n % 2 == 0) {
        it.remove(); // Suppression sûre de l’élément courant
    }
}
System.out.println(numbers); // [1, 3, 5]

Point important : on ne peut supprimer des éléments que via l’itérateur lui-même (it.remove()) et uniquement après avoir appelé it.next(). Si vous essayez d’appeler remove() deux fois de suite sans next(), vous obtiendrez une IllegalStateException.

3. ListIterator : fonctionnalités avancées

Et voici la nouveauté annoncée en début de leçon ! ListIterator est un itérateur « amélioré » pour les listes (List) qui permet non seulement de supprimer, mais aussi d’ajouter des éléments pendant le parcours, ainsi que de se déplacer dans les deux sens (avant et arrière).

Différences avec un Iterator classique

  • Iterator — simple et inflexible : uniquement en avant, uniquement suppression.
  • ListIterator — souple et agile : avant et arrière, suppression, ajout via add(), et possibilité de remplacer l’élément courant avec set().

Exemple : suppression et ajout d’éléments

List<String> words = new ArrayList<>(List.of("cat", "dog", "bird"));

ListIterator<String> it = words.listIterator();
while (it.hasNext()) {
    String word = it.next();
    if (word.length() == 3) {
        it.remove(); // On supprime les mots de 3 lettres
        it.add("pet"); // On ajoute aussitôt "pet" après le mot supprimé
    }
}
System.out.println(words); // [pet, pet, bird]

Remarque : l’ajout via it.add() insère l’élément immédiatement après la position actuelle de l’itérateur.

4. Suppression avec removeIf

Depuis Java 8, une méthode removeIf concise et pratique est disponible. Elle prend une lambda (ou tout Predicate) et supprime tous les éléments pour lesquels la condition renvoie true.

Exemple : suppression de tous les nombres pairs

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));

numbers.removeIf(n -> n % 2 == 0);

System.out.println(numbers); // [1, 3, 5]

Ce n’est pas seulement plus court, mais aussi sûr : en interne, la méthode utilise le bon itérateur — aucune ConcurrentModificationException.

Exemple : suppression des chaînes de moins de 3 caractères

List<String> words = new ArrayList<>(List.of("hi", "cat", "no", "elephant"));

words.removeIf(word -> word.length() < 3);

System.out.println(words); // [cat, elephant]

Conseil : si vous avez simplement besoin de supprimer des éléments selon une condition — utilisez removeIf. C’est la méthode la plus concise et moderne.

5. Recommandations pratiques

Quelle méthode privilégier ?

  • Si vous devez supprimer des éléments selon une condition complexe et que vous utilisez Java 8+ : utilisez removeIf — court, clair, sûr.
  • Si vous êtes sur une ancienne version de Java ou avez besoin d’une logique de parcours plus complexe : utilisez Iterator et sa méthode remove().
  • Si vous travaillez avec une List et souhaitez non seulement supprimer mais aussi ajouter des éléments pendant le parcours : utilisez ListIterator.

Particularités selon les types de collections

  • List : prend en charge toutes les approches décrites (Iterator, ListIterator, removeIf).
  • Set : pas d’indices, mais les Iterator standard et removeIf fonctionnent.
  • Map : pour supprimer selon une condition, utilisez l’itérateur sur entrySet() :
    Map<String, Integer> map = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3));
    Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<String, Integer> entry = it.next();
        if (entry.getValue() % 2 == 0) {
            it.remove();
        }
    }
    System.out.println(map); // {a=1, c=3}
    
    Et avec Java 8+, cela se simplifie grandement :
    map.entrySet().removeIf(entry -> entry.getValue() % 2 == 0);
    

6. Exemple pratique : filtrer des utilisateurs

Supposons que nous ayons une liste d’utilisateurs et que nous voulions supprimer tous les utilisateurs de moins de 18 ans.

class User {
    String name;
    int age;
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

List<User> users = new ArrayList<>(List.of(
    new User("Anna", 17),
    new User("Bob", 20),
    new User("Victoria", 15),
    new User("George", 25)
));

// On supprime les mineurs via removeIf
users.removeIf(user -> user.age < 18);

System.out.println(users); // [Bob (20), George (25)]

7. Comparaison des approches

Faisons un petit tableau récapitulatif. Notre cerveau aime ça.

Méthode Pris en charge depuis Concision Sécurité Flexibilité
for-each + remove()
Java 5+ - -
Iterator + remove()
Java 5+ + +
ListIterator
Java 5+ + ++
removeIf
Java 8+ ++ +

8. Erreurs typiques lors de la suppression d’éléments des collections

Erreur n° 1 : tenter de supprimer des éléments dans for-each

for (String s : list) {
    if (s.equals("test")) {
        list.remove(s); 
    }
}

Vous le savez désormais : à ne pas faire — vous obtiendrez une ConcurrentModificationException ! Utilisez un itérateur ou removeIf.

Erreur n° 2 : appeler remove() sur l’itérateur sans next()

Iterator<String> it = list.iterator();
it.remove(); // IllegalStateException — impossible de supprimer avant l’appel à next()

Erreur n° 3 : tenter de supprimer des éléments d’une collection non modifiable

List<String> immutable = List.of("a", "b", "c");
immutable.removeIf(s -> s.equals("a")); // UnsupportedOperationException

Les méthodes de suppression ne sont pas prises en charge pour les collections immuables.

Erreur n° 4 : tenter de supprimer des éléments d’une Map via values() ou keySet() sans itérateur

for (String key : map.keySet()) {
    if (key.startsWith("a")) {
        map.remove(key); // ConcurrentModificationException!
    }
}

Utilisez un itérateur sur entrySet() ou removeIf.

1
Mission
JAVA 25 SELF, niveau 28, leçon 2
Bloqué
Requalification des animaux de compagnie dans la boutique en ligne "Drug" 🐶
Requalification des animaux de compagnie dans la boutique en ligne "Drug" 🐶
1
Mission
JAVA 25 SELF, niveau 28, leçon 2
Bloqué
Nettoyage de la liste des participants des résultats "suspects" 🏅
Nettoyage de la liste des participants des résultats "suspects" 🏅
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION