C List - Removing items while looping / iterating
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Removing elements from a List<T> while iterating is a frequent source of runtime errors and skipped items. The wrong loop shape can invalidate enumeration or mis-handle shifted indexes. Safe removal depends on choosing the right mutation pattern for your use case.
Why foreach Removal Throws
foreach uses an enumerator that assumes collection structure does not change during iteration. Removing items invalidates the enumerator and throws InvalidOperationException.
So do not structurally mutate list inside foreach.
Best Default: RemoveAll
For predicate-based removals, RemoveAll is concise and safe.
This is usually the best in-place option when condition is straightforward.
Reverse for Loop for Index-Sensitive Logic
When deletion logic depends on index context, iterate backward.
Backward iteration avoids index-shift skip bugs after removal.
Controlled Forward while Loop
A forward loop can work if you only increment index when no removal occurs.
This pattern is useful for stateful scans where RemoveAll is too limited.
Immutable Filtering Alternative
If mutating original list is undesirable, create a filtered list instead.
This is often easier in functional pipelines, but allocates new memory.
Performance Considerations
RemoveAt shifts elements, so repeated removals can be costly for very large lists. RemoveAll is often more efficient than many individual RemoveAt calls because it compacts in one pass.
For heavy workloads:
- Prefer batch predicate removal where possible.
- Separate selection and mutation phases.
- Profile before introducing custom structures.
In many applications, UI redraw or persistence overhead is larger than list mutation cost.
Decision Guide
Choose pattern by requirement:
- Simple predicate and in-place mutation uses
RemoveAll. - Index-aware logic uses reverse
for. - Stateful forward scan uses controlled
while. - Preserve source list uses LINQ filtering.
Using one clear pattern per method improves readability and correctness.
Common Pitfalls
- Removing inside
foreachand hitting enumerator invalidation. - Forward loops that increment blindly and skip shifted elements.
- Using value-based removal when duplicate occurrence control is required.
- Mixing business side effects and mutation logic in one opaque loop.
- Reallocating filtered lists unnecessarily in hot paths.
Summary
- Do not mutate
List<T>structure duringforeachiteration. - Use
RemoveAllas default for predicate-based removals. - Use reverse iteration for index-sensitive deletions.
- Use immutable filtering when original data should remain unchanged.
- Account for index shifting explicitly in manual loop-based removal logic.

