C# List RemoveAt: Removing Elements by Index
c# list removeat: Learn how List<T>.RemoveAt works, its performance characteristics, common pitfalls, and when to use alternatives.
The List<T>.RemoveAt method in C# removes the element at a specified index and shifts all subsequent elements left to fill the gap. It is a fundamental operation when working with List<T>, but its behavior and cost are often misunderstood. This article explains how c# list removeat works, where it fits in your code, and the pitfalls that can lead to bugs or unnecessary performance overhead.
How RemoveAt Behaves
RemoveAt takes a single integer parameter representing the zero-based index of the element to remove. It modifies the list in place and reduces the Count by one. The element at the given index is removed, and every element after it is shifted one position to the left. The capacity of the internal array does not change, so removing an element does not free memory immediately.
var numbers = new List<int> { 10, 20, 30, 40, 50 }; numbers.RemoveAt(2); // numbers now contains: 10, 20, 40, 50
The method throws an ArgumentOutOfRangeException if the index is less than zero or greater than or equal to the current Count. This is the most common exception you will encounter when using RemoveAt.
When to Use RemoveAt
RemoveAt is the right choice when you already know the exact index of the element you want to remove. This often happens when you are processing a list in a loop, or when you have stored the index from a previous search operation. For example, if you are maintaining a list of selected items and you have the index from a UI selection, RemoveAt is direct and efficient.
Another common use case is removing the last element of a list. Since the last element has no following elements to shift, RemoveAt(list.Count - 1) is an O(1) operation. This is a convenient way to implement a stack-like behavior with a list.
Performance Characteristics
The runtime cost of RemoveAt depends on where the removed element is located. Removing the last element is O(1) because no shifting is required. Removing the first element is O(n) because every remaining element must be moved one position left. In general, the cost is proportional to the number of elements after the removed index.
This shifting behavior is not a performance flaw; it is a direct consequence of the contiguous memory layout of List<T>. If you frequently remove elements from the beginning of a large collection, a Queue<T> or a LinkedList<T> may be more appropriate. However, for most scenarios, the simplicity of List<T> outweighs the cost of occasional shifts.
Common Mistakes and How to Avoid Them
One of the most frequent errors is using RemoveAt inside a forward for loop without adjusting the loop variable. When you remove an element at index i, the element that was at i + 1 moves into position i. If you then increment i, you skip the next element.
var list = new List<int> { 1, 2, 3, 4, 5 }; for (int i = 0; i < list.Count; i++) { if (list[i] % 2 == 0) { list.RemoveAt(i); // bug: skipping elements } }
The correct approach is to iterate backward, or to decrement i after a removal. Iterating backward is often cleaner because the indices of elements before the current one are unaffected.
for (int i = list.Count - 1; i >= 0; i--) { if (list[i] % 2 == 0) { list.RemoveAt(i); } }
Another common mistake is assuming that RemoveAt returns the removed element. It returns void. If you need the removed value, retrieve it before calling RemoveAt, or use an indexer to capture it first.
Alternatives to RemoveAt
List<T> provides other removal methods that may fit different scenarios better. Remove(T item) removes the first occurrence of a specific value, but it performs a linear search and then uses RemoveAt internally. If you only have the value and not the index, Remove is convenient, but be aware of the O(n) search cost.
RemoveAll(Predicate<T> match) removes all elements that match a condition. It also shifts elements, but it does so in a single pass, which is more efficient than calling RemoveAt repeatedly in a loop. If you need to remove multiple items based on a condition, RemoveAll is usually the better choice.
If you frequently need to remove elements from the middle of a collection and you care about insertion and removal performance, consider LinkedList<T>. Its node-based structure allows O(1) removal once you have a reference to the node, but it has higher memory overhead and slower random access.
Working with RemoveAt in Loops
When you need to remove elements while iterating, the safest pattern is to iterate backward. This avoids index shifting issues and is easy to reason about. Alternatively, you can use a while loop with a manual index adjustment, but backward iteration is more idiomatic.
var items = new List<string> { "a", "b", "c", "d" }; for (int i = items.Count - 1; i >= 0; i--) { if (items[i].StartsWith("b")) { items.RemoveAt(i); } }
If you are using foreach, you cannot modify the collection during iteration because it will throw an InvalidOperationException. In that case, you should collect the indices or items to remove and then process them after the loop.
Edge Cases and Compatibility
RemoveAt works with any type T, including reference types and nullable value types. It does not set the removed slot to default before shifting; the internal array simply overwrites the slot with the next element. This means that if you hold a reference to the removed object, it remains alive as long as you keep that reference.
For very large lists, the shifting operation can be noticeable, especially if you remove many elements from the front. If you are building a list and removing elements as part of a filtering process, consider using LINQ's Where to create a new list instead of mutating the original. This avoids repeated shifting and can be more readable, though it allocates a new list.
RemoveAt is available in all modern .NET versions and in .NET Framework. There is no version-specific behavior to worry about; the method has been stable since List<T> was introduced.