Aikido

Why you should use safe patterns when removing items from collections

Readability

Rule
Use safe methods when removing from collections.
Modifying a collection while iterating over it often causes bugs.

Supported languages: PY, Java, C/C++, C#, 
Swift/Objective-C, Ruby, PHP, Kotlin, go,
Scala, Rust, Groovy, Dart, Julia, Elixit, 
Erlang, Clojure, OCaml, Lua

Introduction

Removing items from a collection during iteration causes concurrent modification exceptions in Java and unpredictable behavior in C#. The iterator maintains an internal pointer that becomes invalid when the underlying collection changes. This leads to skipped elements, crashes, or infinite loops depending on the collection type and removal pattern used.

Why it matters

System stability: Concurrent modification exceptions crash the application immediately. In production, this means dropped requests and service unavailability. The exception often occurs in edge cases with specific data, making it difficult to catch during testing.

Data integrity: When removal logic fails mid-iteration, the collection is left in a partially modified state. Some items are removed while others that should have been removed remain. This creates inconsistent data that affects downstream logic.

Debugging complexity: Concurrent modification bugs are timing-dependent and may only manifest with certain data combinations. They're difficult to reproduce consistently, making them hard to debug and fix reliably.

Code examples

❌ Non-compliant:

List<User> users = getUserList();
for (User user : users) {
    if (!user.isActive()) {
        users.remove(user); // ConcurrentModificationException
    }
}

Why it's wrong: Removing from users while iterating with enhanced for-loop causes ConcurrentModificationException. The iterator detects that the collection was modified outside the iterator and throws immediately. Any active users after the first inactive one are never processed.

✅ Compliant:

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
    }
}

Why this matters: Using iterator.remove() safely removes items during iteration. The iterator maintains consistent state and continues processing remaining items. All inactive users are correctly removed without exceptions.

Conclusion

Use iterator's remove() method for safe removal during iteration. Alternatively, use streams with filter() to create new collections or removeIf() for bulk removal. Never call collection's remove() directly while iterating.

FAQs

Got Questions?

What about using regular for loops with index?

Iterating backward with index works: for (int i = list.size() - 1; i >= 0; i--). Removing items shifts subsequent elements, but backward iteration avoids skipping. However, iterator.remove() or removeIf() are clearer and less error-prone.

Can I use Java 8+ removeIf instead?

Yes, users.removeIf(user -> !user.isActive()) is the preferred modern approach. It's more concise and handles iteration safely internally. Use removeIf() when removing based on a predicate, streams for transformations, and iterator methods when removal logic is complex.

Does this apply to all collection types?

Yes, ArrayList, HashSet, HashMap, and most collections throw ConcurrentModificationException when modified during iteration. Thread-safe collections like ConcurrentHashMap allow modification but have different semantics. Always check collection documentation for modification rules.

What about C# collections?

C# throws InvalidOperationException when collections are modified during iteration. Use ToList() to create a copy before iterating: foreach (var user in users.ToList()) then remove from original. Or use LINQ: users = users.Where(u => u.IsActive).ToList().

How do I remove multiple items efficiently?

Use removeIf() for single collection or streams for complex filtering: users = users.stream().filter(User::isActive).collect(Collectors.toList()). These approaches are optimized for bulk operations and more efficient than removing items one by one in a loop.

Get secure for free

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.