SkillByAIOpen interactive version →

Lesson 13 / 25

Choosing a Collection

Pick the right collection type and use collection expressions.

Different shapes for different jobs

Arrays (int[]) have a fixed length and fast indexed access. List<T> is a growable array: fast index and append, slow inserts in the middle. Dictionary<TKey, TValue> maps keys to values with near constant-time lookup; use TryGetValue instead of checking then indexing. HashSet<T> stores unique items with fast Contains. Queue<T> (first in, first out), Stack<T> (last in, first out) and PriorityQueue<TElement, TPriority> serve algorithms and schedulers. LinkedList<T> is rarely the right answer. For values that must not change, return IReadOnlyList<T> or use the immutable collections (ImmutableList<T>) or the read-optimised FrozenDictionary and FrozenSet. Collection expressions (C# 12) create most collections with one syntax: int[] a = [1, 2, 3];, List<string> names = [];, and the spread element .. combines sequences.

Collections by access pattern

Pick by how you read and write: by position, by key, by uniqueness or by order of arrival.

Figure 5.1 — Lists, dictionaries, sets and queues.

Collections in everyday code

Collection expressions work for arrays, lists, spans and more.

int[] marks = [72, 45, 90];
List<string> cities = ["Pune", "Delhi"];
List<string> more = [.. cities, "Chennai"];     // spread

var stock = new Dictionary<string, int> { ["pen"] = 120, ["notebook"] = 40 };
if (stock.TryGetValue("pen", out int pens)) Console.WriteLine(pens);
stock["eraser"] = 15;                          // add or overwrite

var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(seen.Add("Asha"));  // True
Console.WriteLine(seen.Add("ASHA"));  // False: duplicate ignoring case

var tasks = new PriorityQueue<string, int>();
tasks.Enqueue("reply to email", 3);
tasks.Enqueue("fix prod bug", 1);
Console.WriteLine(tasks.Dequeue());   // fix prod bug

Contains on a List is a linear scan

list.Contains(x) checks every element. Inside a loop over another large list that becomes quadratic. Put the lookup values in a HashSet<T> first.

Quick check: You need to check thousands of times whether an ID has been seen. Which collection fits best?

  • HashSet<T>
  • List<T>
  • Queue<T>
  • LinkedList<T>
Answer

HashSet<T> — HashSet<T> offers near constant-time membership checks.