The System.Collections.Generic namespace provides type-safe
collection classes. Use these instead of arrays when you need dynamic
sizing.
List<T>
A dynamic array — the most commonly used collection:
CSHARP
var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Adding and removing
numbers.Add(6); // add one element
numbers.AddRange(new[] { 7, 8 }); // add multiple
numbers.Remove(3); // remove first occurrence of 3
numbers.RemoveAt(0); // remove by index
numbers.Clear(); // remove everything
// Querying
numbers.Count; // number of elements
numbers.Contains(5); // true or false
numbers.IndexOf(5); // position, or -1
numbers.First(); // first element (throws if empty)
numbers.FirstOrDefault(); // first element, or default (0 for int)
numbers.Last(); // last elementIterating
CSHARP
var cities = new List<string> { "Kathmandu", "Pokhara", "Chitwan" };
// foreach
foreach (var city in cities)
{
Console.WriteLine(city);
}
// for (when you need the index)
for (int i = 0; i < cities.Count; i++)
{
Console.WriteLine($"{i}: {cities[i]}");
}Sorting
CSHARP
numbers.Sort(); // ascending
numbers.Sort((a, b) => b.CompareTo(a)); // descending
// LINQ ordering (does not modify the original)
var sorted = cities.OrderBy(c => c).ToList();
var desc = cities.OrderByDescending(c => c).ToList();Dictionary<TKey, TValue>
A key-value map — fast lookups by key:
CSHARP
var prices = new Dictionary<string, double>
{
["Notebook"] = 150,
["Pen"] = 25,
["Bag"] = 800,
};
// Accessing
double penPrice = prices["Pen"]; // 25
// Safe access (avoids KeyNotFoundException)
if (prices.TryGetValue("Book", out double price))
{
Console.WriteLine(price);
}
else
{
Console.WriteLine("Book not found");
}
// Adding and removing
prices["Eraser"] = 10;
prices.Remove("Pen");
// Iterating
foreach (var (item, cost) in prices) // deconstruction
{
Console.WriteLine($"{item}: {cost}");
}Array
Fixed-size — use when the size is known at compile time:
CSHARP
int[] nums = { 10, 20, 30, 40, 50 };
Array.Sort(nums);
Array.Reverse(nums);
Array.IndexOf(nums, 30);
Array.Resize(ref nums, 10);Queue<T> and Stack<T>
CSHARP
// Queue — First In, First Out (FIFO)
var queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
string next = queue.Dequeue(); // "First"
// Stack — Last In, First Out (LIFO)
var stack = new Stack<string>();
stack.Push("Bottom");
stack.Push("Top");
string top = stack.Pop(); // "Top"HashSet<T>
An unordered collection of unique elements:
CSHARP
var unique = new HashSet<string> { "apple", "banana", "apple" };
Console.WriteLine(unique.Count); // 2 — duplicates removed
unique.Add("cherry");
unique.Contains("banana"); // trueChoosing the Right Collection
| Collection | Use when… | Lookup | Insert |
|---|---|---|---|
List<T> |
Ordered, dynamic size | O(n) by value | O(1) at end |
Dictionary<K,V> |
Key-value pairs, fast lookup | O(1) by key | O(1) |
HashSet<T> |
Unique elements, fast contains | O(1) | O(1) |
Queue<T> |
FIFO processing | O(n) | O(1) |
Stack<T> |
LIFO processing | O(n) | O(1) |
Array |
Fixed size, performance | O(1) by index | N/A |
Tips
- Use
List<T>as your default — it handles most cases. - Use
Dictionary<K,V>when you need fast key lookups. - Use
HashSet<T>for deduplication and fast membership tests. - Use
TryGetValueon dictionaries instead of indexing +ContainsKey.
Next: LINQ — query your collections with SQL-like syntax.