C# Dictionary guide — add, lookup, iterate, and common patterns

Dictionary<TKey, TValue> is the go-to collection for fast key-based lookups. It provides O(1) average-case performance for add, remove, and lookup using a hash table internally.

Create and Add Entries

C# Example Code
// Create with initial capacity (avoids resizing when size is known)
var scores = new Dictionary<string, int>();

// Add entries
scores.Add("Alice", 95);
scores.Add("Bob",   87);
scores.Add("Carol", 92);

// Collection initializer syntax
var config = new Dictionary<string, string>
{
    ["host"]     = "localhost",
    ["port"]     = "5432",
    ["database"] = "mydb"
};

Add throws ArgumentException if the key already exists. Use the indexer [key] = value to add or overwrite:

C# Example Code
scores["Alice"] = 98;   // update existing
scores["Dave"]  = 76;   // add new

TryGetValue — Safe Lookup Without Exceptions

Avoid double-lookup by combining existence check and retrieval in one call.

C# Example Code
// BAD — checks twice (ContainsKey + indexer)
if (scores.ContainsKey("Alice"))
{
    int s = scores["Alice"];    // ❌ second hash lookup
}

// GOOD — single lookup
if (scores.TryGetValue("Alice", out int score))
{
    Console.WriteLine($"Alice: {score}");   // Alice: 98
}
else
{
    Console.WriteLine("Not found");
}

ContainsKey and ContainsValue

C# Example Code
Console.WriteLine(scores.ContainsKey("Bob"));    // True
Console.WriteLine(scores.ContainsKey("Eve"));    // False

// ContainsValue is O(n) — scans all values
Console.WriteLine(scores.ContainsValue(87));     // True

Remove Entries

C# Example Code
bool removed = scores.Remove("Dave");
Console.WriteLine(removed); // True

// Remove and retrieve the value in one call (.NET 5+)
if (scores.Remove("Bob", out int bobScore))
    Console.WriteLine($"Removed Bob with score {bobScore}"); // 87

Iterating a Dictionary

C# Example Code
// Iterate key-value pairs (order is not guaranteed)
foreach (KeyValuePair<string, int> pair in scores)
    Console.WriteLine($"{pair.Key}: {pair.Value}");

// Deconstruct the pair (C# 7+)
foreach (var (name, value) in scores)
    Console.WriteLine($"{name}: {value}");

// Keys only
foreach (string key in scores.Keys)
    Console.WriteLine(key);

// Values only
foreach (int val in scores.Values)
    Console.WriteLine(val);

GetValueOrDefault — Fallback Without Try/Catch

C# Example Code
int aliceScore = scores.GetValueOrDefault("Alice", 0);   // 98
int eveScore   = scores.GetValueOrDefault("Eve",   0);   // 0 (default)

// Without a default, returns the type default (0 for int, null for reference types)
int missing = scores.GetValueOrDefault("Nobody");        // 0

TryAdd — Add Only If Key Is Absent

C# Example Code
bool added = scores.TryAdd("Frank", 81);  // True — added
bool dup   = scores.TryAdd("Alice", 55);  // False — Alice already exists, value unchanged
Console.WriteLine(scores["Alice"]);        // 98

Common Patterns

Counting Occurrences

C# Example Code
string[] words = { "apple", "banana", "apple", "cherry", "banana", "apple" };

var counts = new Dictionary<string, int>();
foreach (string word in words)
{
    counts.TryGetValue(word, out int current);
    counts[word] = current + 1;
}

// Shorter with GetValueOrDefault
foreach (string word in words)
    counts[word] = counts.GetValueOrDefault(word) + 1;

foreach (var (word, count) in counts)
    Console.WriteLine($"{word}: {count}");
// apple: 3, banana: 2, cherry: 1

Grouping Items by a Key

C# Example Code
var people = new[]
{
    new { Name = "Alice", Dept = "Engineering" },
    new { Name = "Bob",   Dept = "Marketing" },
    new { Name = "Carol", Dept = "Engineering" },
    new { Name = "Dave",  Dept = "Marketing" },
};

var byDept = new Dictionary<string, List<string>>();
foreach (var person in people)
{
    if (!byDept.ContainsKey(person.Dept))
        byDept[person.Dept] = new List<string>();
    byDept[person.Dept].Add(person.Name);
}

// With LINQ — same result, more concise
var byDeptLinq = people
    .GroupBy(p => p.Dept)
    .ToDictionary(g => g.Key, g => g.Select(p => p.Name).ToList());

Lookup Table / Dispatcher

C# Example Code
var handlers = new Dictionary<string, Action<string>>
{
    ["greet"]  = name => Console.WriteLine($"Hello, {name}!"),
    ["shout"]  = name => Console.WriteLine(name.ToUpper()),
    ["reverse"] = name => Console.WriteLine(new string(name.Reverse().ToArray())),
};

string command = "greet";
string arg     = "World";

if (handlers.TryGetValue(command, out var handler))
    handler(arg);   // Hello, World!
else
    Console.WriteLine($"Unknown command: {command}");

Nested Dictionaries

C# Example Code
// Matrix: row key → column key → value
var matrix = new Dictionary<string, Dictionary<string, double>>
{
    ["Alice"] = new() { ["Math"] = 95.0, ["Science"] = 88.5 },
    ["Bob"]   = new() { ["Math"] = 72.0, ["Science"] = 91.0 },
};

if (matrix.TryGetValue("Alice", out var row) &&
    row.TryGetValue("Math", out double mathScore))
{
    Console.WriteLine($"Alice Math: {mathScore}"); // 95
}

Quick Reference

OperationCode
Add (throws if duplicate)dict.Add(key, value)
Add or overwritedict[key] = value
Safe add (no overwrite)dict.TryAdd(key, value)
Safe lookupdict.TryGetValue(key, out var val)
Lookup with fallbackdict.GetValueOrDefault(key, fallback)
Key exists?dict.ContainsKey(key)
Remove entrydict.Remove(key)
Remove + retrievedict.Remove(key, out var val)
Iterate pairsforeach (var (k, v) in dict)
Count entriesdict.Count
Empty the dictionarydict.Clear()