C# Collections: Arrays, List, Dictionary and HashSet
Codevity Admin · 10/25/2026
Codevity Admin · 10/25/2026
Choosing and explaining the right collection is a staple of C# interviews. This article covers the types you must know and the questions that come with them.
T[] (array) | List<T> | |
|---|---|---|
| Size | Fixed when created | Grows and shrinks |
| Add or remove | Not possible (you must create a new array) | Add, Insert, Remove, RemoveAt |
| Access by index | O(1) | O(1) |
| Where it shines | Fixed-size data, performance-critical code, interop | Almost everything else |
A List<T> is a wrapper around an array. When the internal array is full, it allocates a bigger one (roughly double) and copies the items, so adding is amortized O(1). If you know the size in advance, pass a capacity (new List<int>(1000)) to avoid repeated resizing.
| You need... | Use |
|---|---|
| Ordered items, indexed access | List<T> |
| Lookup by key | Dictionary<TKey, TValue> |
| Unique items, fast "contains" | HashSet<T> |
| First in, first out | Queue<T> |
| Last in, first out (undo, parsing brackets) | Stack<T> |
| Items kept sorted by key | SortedDictionary<TKey, TValue> or SortedSet<T> |
A Dictionary<TKey, TValue> is a hash table:
key.GetHashCode() and uses the result to choose a bucket.Equals() to find the right one.That is why a key type must implement GetHashCode() and Equals() consistently: keys that are equal must produce the same hash code. A class that does not override them uses reference equality, so two "equal-looking" objects are different keys:
var classMap = new Dictionary<CoordClass, string>();
classMap[new CoordClass(1, 2)] = "A";
Console.WriteLine(classMap.ContainsKey(new CoordClass(1, 2))); // False (reference equality)
var recordMap = new Dictionary<CoordRecord, string>();
recordMap[new CoordRecord(1, 2)] = "A";
Console.WriteLine(recordMap.ContainsKey(new CoordRecord(1, 2))); // True (records compare by value)
public class CoordClass
{
public CoordClass(int x, int y) { X = x; Y = y; }
public int X { get; }
public int Y { get; }
}
public record CoordRecord(int X, int Y);Records and structs get value equality for free. For a normal class, override both Equals and GetHashCode, and keep the fields they use immutable: mutating a key after inserting it makes it unfindable.
Common dictionary operations:
var ages = new Dictionary<string, int> { ["Asha"] = 31, ["Ben"] = 24 };
if (ages.TryGetValue("Asha", out int age)) // safe lookup, no exception
Console.WriteLine(age); // 31
Console.WriteLine(ages.GetValueOrDefault("Zed", -1)); // -1
ages["Carla"] = 45; // adds or overwrites
Console.WriteLine(ages.Count); // 3ages["Zed"] throws KeyNotFoundException for a missing key, so use TryGetValue when a key might be absent.
A HashSet<T> stores unique items and answers "is it in there?" in O(1) on average. Its method returns if the item was already present, which makes it ideal for finding duplicates:
| Interface | Adds | Use it when you only need to... |
|---|---|---|
| Iterate with | Read items one by one. This is the most general parameter type | |
| , , , | Know the size or change the collection | |
| Index access, , | Read or write by position |
Accept the least specific type that does the job (usually or ), and return a concrete type that is useful to the caller.
over a detects that the collection changed and throws. Use , loop backwards with a loop, or build a new list.
AddfalseIEnumerable<T>foreachICollection<T>CountAddRemoveContainsIList<T>InsertRemoveAtIEnumerable<T>IReadOnlyList<T>foreachList<T>RemoveAllforDictionary<char, int>EqualsGetHashCode// Prints "duplicate: 3" and then "duplicate: 1".
var seen = new HashSet<int>();
foreach (int n in new[] { 3, 1, 3, 2, 1 })
{
if (!seen.Add(n))
Console.WriteLine($"duplicate: {n}");
}var numbers = new List<int> { 1, 2, 3, 4 };
try
{
foreach (var n in numbers)
{
if (n == 2) numbers.Remove(n); // modifies the list during enumeration
}
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().Name); // InvalidOperationException
}
numbers.RemoveAll(n => n % 2 == 0); // the correct way to remove by condition
Console.WriteLine(string.Join(", ", numbers)); // 1, 3