List / Dictionary
C# collections: List, Dictionary, HashSet, Queue, Stack, ImmutableX, ReadOnlyX. The right structure for the operation.
C# — collections
EXAMPLE
using System.Collections.Generic;
using System.Collections.Immutable;
// ===== List<T> (most common) =====
var nums = new List<int> { 1, 2, 3, 4 };
nums.Add(5);
nums.RemoveAt(0);
nums.Contains(3);
nums.IndexOf(3);
// ===== Dictionary<K,V> =====
var ages = new Dictionary<string, int> {
["Alex"] = 30,
["Sam"] = 25,
};
ages["Alex"]; // 30
ages.TryGetValue("Lee", out var age);
ages.ContainsKey("Alex");
ages.Remove("Sam");
// ===== HashSet<T> =====
var tags = new HashSet<string> { "vip", "beta" };
tags.Add("new");
tags.Contains("vip");
tags.UnionWith(new[] { "alpha", "new" });
tags.IntersectWith(new[] { "vip", "new" });
// ===== Queue<T> and Stack<T> =====
var q = new Queue<string>();
q.Enqueue("a"); q.Enqueue("b");
q.Dequeue(); // 'a' (FIFO)
var s = new Stack<string>();
s.Push("a"); s.Push("b");
s.Pop(); // 'b' (LIFO)
// ===== SortedDictionary<K,V> + SortedSet<T> =====
var scores = new SortedDictionary<string, int>(); // keys in sorted order
scores["alex"] = 100;
scores["bob"] = 50;
foreach (var (k, v) in scores) { /* ordered */ }
// ===== Concurrent collections =====
using System.Collections.Concurrent;
var d = new ConcurrentDictionary<string, int>();
d.TryAdd("a", 1);
d.AddOrUpdate("a", 1, (k, old) => old + 1);
var bag = new ConcurrentBag<int>(); // unordered, thread-safe
var cq = new ConcurrentQueue<int>();
// ===== Immutable collections =====
var im = ImmutableList.Create(1, 2, 3);
var im2 = im.Add(4); // returns new list
// im is unchanged
ImmutableArray.Create(1, 2, 3);
ImmutableDictionary<string, int>.Empty.Add("a", 1);
// ===== ReadOnly views =====
List<int> data = new() { 1, 2, 3 };
IReadOnlyList<int> view = data; // read-only interface; data is still mutable
// ===== LINQ over collections =====
using System.Linq;
var evens = nums.Where(n => n % 2 == 0).ToList();
var sum = nums.Sum();
var byTag = users.GroupBy(u => u.Tag).ToDictionary(g => g.Key, g => g.Count());
// ===== When to use what =====
// List<T> Default sequential collection
// Dictionary<K,V> Key-value; O(1) average lookup
// HashSet<T> Unique items; O(1) average lookup
// Queue / Stack FIFO / LIFO semantics
// SortedX When key order matters; O(log n) ops
// Concurrent Multi-threaded access
// Immutable Functional / persistent data structures
// ===== Patterns to internalise =====
// - Pick the structure based on the HOT operation
// - IReadOnlyList / IReadOnlyDictionary for public API surface
// - Concurrent collections instead of locking around regular ones
// - ImmutableX for snapshots / undo / sharing across threads
// ===== Pitfalls =====
// - List<T> as a hash-like API (no fast lookup; use Dictionary)
// - Modifying a collection while iterating it -> exception
// - Public List<T> property -> consumers can mutate; expose IReadOnlyList<T>
// - ConcurrentDictionary lookups in a loop without TryGetValue -> race conditions
Why it matters
C# has a rich collection toolbox. List + Dictionary + HashSet cover most needs; Queue + Stack for FIFO/LIFO; Sorted variants for ordered keys; Concurrent for threads; Immutable for snapshots. Pick by the hot operation, expose IReadOnly views to public APIs, and the code stays predictable.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
var list = new List<string> { "a", "b" };
list.Add("c");
var ages = new Dictionary<string, int>();
ages["ada"] = 36;
Try it Yourself »
Discussion
Loading…