List Method in C#
What is List<T>?
In C#, List<T> is a generic collection class that provides dynamic array-like behavior. you can grow, shrink and manipulate elements easily. It belongs to the System.Collections.Generic namespace. Explore more about List in C#
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>
List Method in C#
Part 1 : Add(), AddRange(), Insert(), InsertRange() and Clear() Methods in C# List<T>
1. Add(T item)
Adds a single element at the end of the list. If the internal array has enough capacity, the item is simply placed at the next index. If not, the list automatically resizes typically doubling its capacity.
Syntaxpublic void Add(T item);
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> languages = new List<string>();
languages.Add("C#");
languages.Add("Java");
languages.Add("Python");
foreach (var lang in languages)
Console.WriteLine(lang);
}
}C# Java Python
Time & Space Complexity
| Operation | Average | Worst Case | Space |
|---|---|---|---|
| Add | O(1) | O(n) (when resizing occurs) | O(1) or O(n) on resize |
2. AddRange(IEnumerable<T> collection)
Adds multiple elements from another collection (like an array, another list or any enumerable) to the end of the list in one go. This is faster than calling Add() repeatedly.
Syntaxpublic void AddRange(IEnumerable<T> collection);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| AddRange | O(k) | O(k) (for k new elements) |
- Much faster than multiple Add() calls due to fewer resizes.
- The method automatically resizes the list capacity as needed.
3. Insert(int index, T item)
Inserts an element at a specific position in the list. All elements after the given index are shifted one position to the right.
public void Insert(int index, T item);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| Insert | O(n) (due to element shifting) | O(1) |
- Avoid inserting frequently in the middle or start it causes shifting.
- For frequent insertions, consider using LinkedList<T> instead.
4. InsertRange(int index, IEnumerable<T> collection)
The InsertRange method allows you to insert multiple elements into a list starting at a specific index. All elements at and after the specified index are shifted forward to make space for the new elements.
public void InsertRange(int index, IEnumerable<T> collection);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| InsertRange | O(n + k) | O(k) |
Note :- InsertRange is efficient for inserting multiple elements at once compared to inserting them individually in a loop.
5. Clear()
The Clear() method removes all elements from a list and resets its Count to zero. It does not reduce the list’s capacity. To free unused memory, you can use TrimExcess() after clearing.
public void Clear();
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Clear() | O(n) | O(1) |
- Clear() removes references to objects, allowing them to be garbage collected.
- The capacity of the list remains unchanged. To reduce memory usage, call TrimExcess().
Real-World Use Case, Example : Building a Dynamic Student List
List<string> students = new List<string>();
// Adding new students dynamically
students.Add("Amit");
students.AddRange(new string[] { "Riya", "Sam" });
// Inserting transfer student at position 1
students.Insert(1, "Rahul");
// Adding late-joining batch
students.InsertRange(3, new string[] { "Kiran", "Divya" });
// Clearing at semester end
students.Clear();Part 2 :- Remove(), RemoveAll(), RemoveAt(), RemoveRange(), TrimExcess() and EnsureCapacity()
6. Remove(T item)
Removes the first occurrence of the specified item from the list. If the item is not found, the list remains unchanged. After removal, all subsequent elements are shifted left by one
public bool Remove(T item);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| Remove | O(n) | O(1) |
- Only removes the first occurrence.
- For multiple occurrences, use RemoveAll().
- Internally shifts remaining elements.
7. RemoveAll(Predicate<T> match)
Removes all elements that satisfy a given condition defined by a predicate.
public int RemoveAll(Predicate<T> match);
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6 };
int removedCount = numbers.RemoveAll(n => n % 2 == 0); // remove even numbers
Console.WriteLine("Removed Count: " + removedCount);
Console.WriteLine(string.Join(", ", numbers));
}
}Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| RemoveAll | O(n) | O(1) |
- Very useful for filtering lists dynamically.
- Predicate must not modify the collection inside.
8. RemoveAt(int index)
The RemoveAt() method removes an element at a specific index in the list. All the elements after that index are shifted left to fill the empty space, maintaining the list’s order.
public void RemoveAt(int index);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| RemoveAt() | O(n) | O(1) |
- Throws ArgumentOutOfRangeException if the index is less than 0 or greater than or equal to Count.
- Best used when removing elements from the end of the list (as it avoids shifting).
- For frequent removals from the middle or start, consider using a LinkedList for better efficiency.
9. RemoveRange(int index, int count)
The RemoveRange() method removes multiple consecutive elements from a list, starting at the specified index. After removal, all remaining elements are shifted left to fill the gap and maintain order.
public void RemoveRange(int index, int count);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| RemoveRange() | O(n) | O(1) |
- Throws ArgumentOutOfRangeException if the index or count is invalid.
- Internally, the remaining elements are shifted to maintain order.
- More efficient than calling RemoveAt() repeatedly in a loop.
10. TrimExcess()
The TrimExcess() method reduces the capacity of a list so that it matches the actual number of elements stored. It helps free unused memory, especially when a list previously held many elements but now contains only a few.
public void TrimExcess();
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| TrimExcess() | O(n) | O(n) |
- Helps optimize memory usage after large removals or clear operations.
- Recommended when you plan to store or serialize a list for long-term use.
- Does not affect the list’s data, only adjusts internal capacity.
11. EnsureCapacity(int capacity)
The EnsureCapacity() method prepares the internal array of a list to accommodate at least the specified number of elements without resizing in the future. It’s especially useful when you know in advance that you’ll be adding a large number of elements. This helps avoid multiple costly resizes and improves overall performance.
public int EnsureCapacity(int min);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| EnsureCapacity() | O(n) (only if resizing occurs) | O(n) |
- Prevents repeated reallocation and copying during large insertions.
- If the requested capacity is less than or equal to the current capacity, nothing changes.
- When no capacity is preset, the list automatically doubles its size as elements are added.
Part 3 : Search & Find Methods in C# List<T>
12. Contains(T item)
The Contains() method checks if a specific item exists in a list. Returns true if the item is found, otherwise false if the item is not present.
public bool Contains(T item);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Contains() | O(n) | O(1) |
- Uses EqualityComparer<T>.Default to check equality.
- Best suited for small or unsorted lists.
- For large or frequently searched lists, consider using a HashSet<T> for faster lookups.
13. Exists(Predicate<T> match)
The Exists() method checks whether any element in the list satisfies a given condition (predicate). Returns true if at least one element matches the condition, otherwise returns false if no elements match.
public bool Exists(Predicate<T> match);
List<int> numbers = new List<int>() { 1, 3, 5, 7 };
// Check if any number is even
bool hasEven = numbers.Exists(n => n % 2 == 0);
Console.WriteLine($"Has Even Number? {hasEven}");Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Exists() | O(n) | O(1) |
- Ideal for predicate-based searches without manually iterating through the list.
- Stops scanning after the first match, making it more efficient than a full search when a match exists.
- Commonly used with lambda expressions for concise and readable code.
14. Find(Predicate<T> match)
The Find() method returns the first element in the list that satisfies a given condition (predicate). If no element matches, it returns default (null for reference types, 0 for numeric types etc.).
public T? Find(Predicate<T> match);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Find() | O(n) | O(1) |
- Returns only the first match.
- Use FindAll() if you need all matching elements.
- Supports lambda expressions or predicates, making it concise for conditional searches.
15. FindAll(Predicate<T> match)
The FindAll() method returns a new list containing all elements that satisfy the specified condition (predicate). The original list remains unchanged, making it safe for filtering without modifying data.
public List<T> FindAll(Predicate<T> match);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| FindAll() | O(n) | O(k) (number of matches) |
- Returns a new List<T>, leaving the original list intact.
- Ideal for filtering elements based on conditions.
- Works well with lambda expressions or predicates for clean, readable code.
16. FindIndex()
The FindIndex() method returns the index of the first element in a list that satisfies a specified condition (predicate). Overloads allow you to specify a starting index and/or a range of elements to search. Returns -1 if no element matches the condition.
public int FindIndex(Predicate<T> match); public int FindIndex(int startIndex, Predicate<T> match); public int FindIndex(int startIndex, int count, Predicate<T> match);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| FindIndex() | O(n) | O(1) |
- Useful when you want to modify, insert or remove elements by index.
- Stops searching after the first match, making it efficient for early matches.
- Works well with lambda expressions for concise code.
17. FindLast() and FindLastIndex()
- FindLast(): Returns the last element in the list that satisfies a given predicate.
- FindLastIndex(): Returns the index of the last element that matches the predicate.
- Both methods scan the list from the end towards the beginning.
- Returns default (null for reference types) or -1 if no match is found.
public T? FindLast(Predicate<T> match); public int FindLastIndex(Predicate<T> match); public int FindLastIndex(int startIndex, Predicate<T> match); public int FindLastIndex(int startIndex, int count, Predicate<T> match);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| FindLast() | O(n) | O(1) |
| FindLastIndex() | O(n) | O(1) |
- Scans from end to start, making it ideal for finding the latest occurrence.
- Useful when you need the last matching element or its index without reversing the list.
- Supports lambda expressions or predicates for concise, readable code.
18. TrueForAll(Predicate<T> match)
The TrueForAll() method checks whether all elements in a list satisfy a specified condition (predicate). Returns true only if every element matches the condition, otherwise returns false if any element fails the condition.
public bool TrueForAll(Predicate<T> match);
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| TrueForAll() | O(n) | O(1) |
- Stops scanning as soon as one element fails the predicate, making it efficient for validation.
- Useful for data validation, filtering and ensuring all items meet a condition.
- Works perfectly with lambda expressions for concise, readable code.
Part 4 : Sorting, BinarySearch and Enumerator Methods in C# List<T>
19. Sort()
The Sort() method sorts the elements of a List<T> in ascending order by default. Overloads allow custom comparisons, partial sorting and using custom comparers. Internally, it uses QuickSort/IntroSort for efficient sorting.
Syntaxpublic void Sort(); // Default sort using IComparable<T> public void Sort(Comparison<T> comparison); // Custom comparison public void Sort(IComparer<T>? comparer); // Custom comparer public void Sort(int index, int count, IComparer<T>? comparer); // Partial sort
class FirstCharComparer : IComparer<string>
{
public int Compare(string? x, string? y)
{
if (x == null || y == null) return 0;
return x[0].CompareTo(y[0]);
}
}
-----------------------------------------------------------------------------------------
// Sample list
List<string> fruits = new List<string>() { "Banana", "Apple", "Cherry", "Mango" };
// 1. Default Sort (IComparable<T>) — Ascending alphabetical
fruits.Sort();
Console.WriteLine("Default Sort: " + string.Join(", ", fruits));
// Output: Apple, Banana, Cherry, Mango
// Reset list
fruits = new List<string>() { "Banana", "Apple", "Cherry", "Mango" };
// 2. Custom Comparison — Sort by descending length
fruits.Sort((a, b) => b.Length.CompareTo(a.Length));
Console.WriteLine("Custom Comparison (by length descending): " + string.Join(", ", fruits));
// Output: Banana, Cherry, Mango, Apple
// Reset list
fruits = new List<string>() { "Banana", "Apple", "Cherry", "Mango" };
// 3. Custom IComparer — Sort by first character
fruits.Sort(new FirstCharComparer());
Console.WriteLine("Custom IComparer (first char ascending): " + string.Join(", ", fruits));
// Output: Apple, Banana, Cherry, Mango
// Reset list
fruits = new List<string>() { "Banana", "Apple", "Cherry", "Mango" };
// 4. Partial Sort — Sort first 3 elements alphabetically
fruits.Sort(0, 3, null); // null uses default comparer
Console.WriteLine("Partial Sort (first 3 elements): " + string.Join(", ", fruits));
// Output: Apple, Banana, Cherry, Mango (only first 3 sorted)Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Sort() | O(n log n) | O(log n) |
- Use Comparison<T> or IComparer<T> for custom sorting logic.
- Partial sorting is helpful when you need to sort only a subset of the list.
- Efficient for both small and large lists due to hybrid sorting algorithms.
20. BinarySearch()
Searches for an element in a sorted list using binary search algorithm. Returns the index if found, otherwise a negative number indicating the complement of the insertion point.
Syntaxpublic int BinarySearch(T item); public int BinarySearch(T item, IComparer<T>? comparer); public int BinarySearch(int index, int count, T item, IComparer<T>? comparer);
using System;
using System.Collections.Generic;
class DescendingComparer : IComparer<int>
{
public int Compare(int x, int y) => y.CompareTo(x);
}
class Program
{
static void Main()
{
// 1. Default BinarySearch (ascending order)
List<int> numbers = new List<int>() { 1, 3, 5, 7, 9 };
int index1 = numbers.BinarySearch(7);
Console.WriteLine($"Default BinarySearch(7) -> Index: {index1}");
// Output: 3
// 2. BinarySearch with custom comparer (descending order)
numbers.Sort(new DescendingComparer()); // sort descending: [9,7,5,3,1]
int index2 = numbers.BinarySearch(7, new DescendingComparer());
Console.WriteLine($"BinarySearch(7, DescendingComparer) -> Index: {index2}");
// Output: 1
// 3. BinarySearch in a range of the list (ascending)
numbers.Sort(); // back to ascending: [1,3,5,7,9]
// search in first 4 elements (indices 0,1,2,3)
int index3 = numbers.BinarySearch(0, 4, 7, Comparer<int>.Default);
Console.WriteLine($"BinarySearch(7, index:0, count:4) -> Index: {index3}");
// Output: 3
// 4. BinarySearch for element not found
int index4 = numbers.BinarySearch(6);
Console.WriteLine($"BinarySearch(6) -> Not found, returns: {index4}");
// Output: -4 (insertion point = 3)
// 5. BinarySearch with element not in range
int index5 = numbers.BinarySearch(0, 3, 7, Comparer<int>.Default);
Console.WriteLine($"BinarySearch(7, index:0, count:3) -> Not in range, returns: {index5}");
// Output: -4 (insertion point = 3)
}
}Default BinarySearch(7) -> Index: 3 BinarySearch(7, DescendingComparer) -> Index: 1 BinarySearch(7, index:0, count:4) -> Index: 3 BinarySearch(6) -> Not found, returns: -4 BinarySearch(7, index:0, count:3) -> Not in range, returns: -4
Time & Space Complexity
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| BinarySearch() | O(log n) | O(1) |
- The list must be sorted before calling BinarySearch().
- Overloads allow custom comparers or search within a subset of the list.
- Ideal for fast searches in large datasets.
21. Reverse()
The Reverse() method in C# reverses the order of elements in a List<T>. You can either reverse the entire list or a specific subrange of elements. This method modifies the list in place, so no new list is created.
public void Reverse(); // Reverses the entire list public void Reverse(int index, int count); // Reverses a subrange of the list
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| Reverse() | O(n) | O(1) |
- Reverses the list in place, no additional memory allocation.
22. GetRange()
The GetRange() method in C# returns a new list containing a specific range of elements from an existing List<T>. The original list remains unchanged, making it ideal for extracting subsets, pagination or chunking large lists.
public List<T> GetRange(int index, int count);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| GetRange() | O(k) | O(k) |
- Creates a new list containing the selected range.
- Original list is not modified.
- Useful for: Pagination of large datasets , Extracting chunks or windows from a list , Temporary sub list operations without affecting the main list
23. ForEach(Action<T> action)
The ForEach() method in C# applies a specified action to each element in a List<T>. It offers a cleaner and more readable alternative to traditional for or foreach loops, especially for simple operations like printing or applying transformations.
public void ForEach(Action<T> action);
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| ForEach() | O(n) | O(1) |
- Cannot break or return early like a traditional for loop.
- Best suited for: Applying transformations to all elements, Printing or logging list items, Simple in-place operations on each element.
24. Enumerator (GetEnumerator())
The GetEnumerator() method in C# provides an enumerator for a List<T>, allowing manual iteration over the elements using MoveNext() and Current. It is the underlying mechanism behind the foreach loop, giving you fine-grained control over iteration.
public List<T>.Enumerator GetEnumerator();
Time & Space Complexity
| Operation | Time | Space |
|---|---|---|
| GetEnumerator() | O(1) | O(1) |
- Provides manual control over list iteration.
- foreach loops are syntactic sugar over GetEnumerator().
- Useful when you need to: Pause/resume iteration, Track iteration state , Avoid modifying the list while iterating.
