Generic Collections in C# – Examples
Generic collections are type-safe collections available in the System.Collections.Generic namespace. They store and manage multiple values of a specified data type.
1. List<T> – Dynamic Array
Stores elements in order, allows duplicates, and supports index-based access.
List<int> numbers = new List<int> { 10, 20, 30 };
numbers.Add(40);
numbers.Remove(20);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
// Output: 10, 30, 40
Key Points: Dynamic size, allows duplicates, maintains insertion order.
2. Dictionary<TKey, TValue> – Key-Value Pairs
Stores data as key-value pairs. Keys must be unique.
Dictionary<int, string> employees = new()
{
{ 101, "John" },
{ 102, "David" },
{ 103, "Peter" }
};
employees.Add(104, "Sam");
Console.WriteLine(employees[102]);
foreach (var emp in employees)
{
Console.WriteLine($"{emp.Key} - {emp.Value}");
}
// Output: David
// 101 - John
// 102 - David
// 103 - Peter
// 104 - Sam
Key Points: Unique keys, fast lookup, values can be duplicated.
3. HashSet<T> – Unique Elements
Stores only unique values. Duplicate values are automatically ignored.
HashSet<int> numbers = new() { 10, 20, 30 };
numbers.Add(20);
numbers.Add(40);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
// Elements: 10, 20, 30, 40
// Order is not guaranteed
Key Points: No duplicates, fast membership checks, no guaranteed ordering.
4. Stack<T> – LIFO
Stack follows Last In, First Out (LIFO). The last inserted element is removed first.
Stack<string> names = new();
names.Push("John");
names.Push("David");
names.Push("Peter");
Console.WriteLine(names.Pop()); // Peter
Console.WriteLine(names.Peek()); // David
Console.WriteLine(names.Count); // 2
Key Points: Push() adds, Pop() removes, Peek() reads the top element without removing it.
5. Queue<T> – FIFO
Queue follows First In, First Out (FIFO). The first inserted element is removed first.
Queue<string> names = new();
names.Enqueue("John");
names.Enqueue("David");
names.Enqueue("Peter");
Console.WriteLine(names.Dequeue()); // John
Console.WriteLine(names.Peek()); // David
Console.WriteLine(names.Count); // 2
Key Points: Enqueue() adds, Dequeue() removes, Peek() reads the front element.
6. LinkedList<T> – Doubly Linked List
Stores elements in nodes linked to their previous and next nodes.
LinkedList<int> numbers = new();
numbers.AddLast(20);
numbers.AddFirst(10);
numbers.AddLast(30);
numbers.Remove(20);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
// Output: 10, 30
Key Points: Efficient insertion and deletion at known nodes, no index-based access.
7. SortedDictionary<TKey, TValue> – Sorted Keys
Stores key-value pairs automatically sorted by key.
SortedDictionary<int, string> students = new()
{
{ 3, "Peter" },
{ 1, "John" },
{ 2, "David" }
};
foreach (var student in students)
{
Console.WriteLine($"{student.Key} - {student.Value}");
}
// Output:
// 1 - John
// 2 - David
// 3 - Peter
Key Points: Unique keys, sorted by key, typically slower lookup than Dictionary<TKey, TValue>.
8. SortedSet<T> – Sorted Unique Values
Stores unique elements in sorted order.
SortedSet<int> numbers = new() { 50, 10, 30, 20 };
numbers.Add(10);
numbers.Add(40);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
// Output: 10, 20, 30, 40, 50
Key Points: Unique values, automatically sorted, supports range operations.
Generic Collections – Quick Comparison
|
Collection |
Duplicates |
Ordering |
Best Use |
|---|---|---|---|
|
|
Yes |
Insertion order |
Dynamic lists |
|
|
Values only |
Not guaranteed |
Key-value lookup |
|
|
No |
Not guaranteed |
Unique values |
|
|
Yes |
LIFO |
Undo operations |
|
|
Yes |
FIFO |
Task processing |
|
|
Yes |
Node order |
Frequent node insertion/removal |
|
|
Values only |
Sorted keys |
Sorted key-value data |
|
|
No |
Sorted |
Unique sorted values |
Important Interview Questions
1. List vs Array? Array has a fixed length. List<T> grows dynamically and provides methods such as Add(), Remove() and Contains().
2. List vs HashSet? List<T> allows duplicates and index-based access. HashSet<T> stores unique values and provides fast membership checks.
3. Dictionary vs SortedDictionary? Dictionary<TKey,TValue> uses hashing for fast lookup. SortedDictionary<TKey,TValue> maintains keys in sorted order.
4. Stack vs Queue? Stack follows LIFO; Queue follows FIFO.
5. Which generic collection is best for searching? Dictionary<TKey,TValue> for key-based lookup and HashSet<T> for checking whether a value exists. Both provide average O(1) lookup.
6. Are generic collections thread-safe? Most generic collections are not thread-safe for concurrent modifications. Use collections such as ConcurrentDictionary<TKey,TValue> or ConcurrentQueue<T> when appropriate.
7. Why use generic collections instead of non-generic collections? Generic collections provide compile-time type safety, reduce casting, and avoid boxing for value types in many common scenarios.