Règle
Utilisation des méthodes lorsque retirer des collections.
Modifier une collection tout en l'itération sur celle-ci souvent provoque des bugs.
Langues prises en charge : PY, Java, C/C++, C#,
Swift/Objective-C, Ruby, PHP, Kotlin, Go,
Scala, Rust, Groovy, Dart, Julia, Elixit,
Erlang, Clojure, OCaml, LuaIntroduction
La suppression d'éléments d'une collection pendant son itération provoque des exceptions de modification concurrente en Java et un comportement imprévisible en C#. L'itérateur maintient un pointeur interne qui devient invalide lorsque la collection sous-jacente est modifiée. Cela peut entraîner des éléments ignorés, des plantages ou des boucles infinies, selon le type de collection et le modèle de suppression utilisé.
Pourquoi c'est important
Stabilité du système : Les exceptions de modification concurrente font planter l'application immédiatement. En production, cela se traduit par des requêtes abandonnées et une indisponibilité du service. L'exception survient souvent dans des cas limites avec des données spécifiques, ce qui la rend difficile à détecter lors des tests.
Intégrité des données : Lorsque la logique de suppression échoue en cours d'itération, la collection est laissée dans un état partiellement modifié. Certains éléments sont supprimés tandis que d'autres qui auraient dû l'être restent. Cela crée des données incohérentes qui affectent la logique en aval.
Complexité du débogage : Les bugs de modification concurrente dépendent du timing et peuvent ne se manifester qu'avec certaines combinaisons de données. Ils sont difficiles à reproduire de manière cohérente, ce qui les rend ardus à déboguer et à corriger de manière fiable.
Exemples de code
❌ Non conforme :
List<User> users = getUserList();
for (User user : users) {
if (!user.isActive()) {
users.remove(user); // ConcurrentModificationException
}
}
Pourquoi c'est incorrect : Suppression de utilisateurs lorsque l'itération avec une boucle for améliorée provoque ConcurrentModificationException. L'itérateur détecte que la collection a été modifiée en dehors de l'itérateur et lève immédiatement une exception. Tout utilisateur actif après le premier inactif n'est jamais traité.
✅ Conforme :
List<User> users = getUserList();
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()) {
User user = iterator.next();
if (!user.isActive()) {
iterator.remove(); // Safe removal through iterator
}
}
Pourquoi c'est important : Utilisation iterator.remove() supprime les éléments en toute sécurité pendant l'itération. L'itérateur maintient un état cohérent et continue de traiter les éléments restants. Tous les utilisateurs inactifs sont correctement supprimés sans exception.
Conclusion
Utilisez les itérateurs remove() méthode pour une suppression sûre pendant l'itération. Alternativement, utilisez des flux avec filter() pour créer de nouvelles collections ou removeIf() pour la suppression en masse. Ne jamais appeler la collection de remove() directement lors de l'itération.
.avif)
