A city doesn't just have residents — it has services indexed by name, unique permits that can't be duplicated, and task queues for processing requests. C# gives us specialized collections for each of these patterns: Dictionary<TKey, TValue> for key-value lookups, HashSet<T> for unique elements, Queue<T> for first-in-first-out processing, and Stack<T> for last-in-first-out undo operations.
Dictionary<TKey, TValue> — Instant Lookups
A Dictionary maps unique keys to values using a hash table internally. Lookups, insertions, and deletions are all O(1) average case. Think of it as the city's phone book — you look up a department name and instantly get its info.
Dictionary<string, int> cityServices = new Dictionary<string, int>();
// Adding entries
cityServices.Add("Fire Station", 5); // 5 trucks
cityServices["Police"] = 10; // 10 officers (indexer syntax)
cityServices["Hospital"] = 20; // 20 beds
// Reading values
Console.WriteLine($"Fire Station: {cityServices["Fire Station"]} trucks");
// Safe lookup with TryGetValue (avoids KeyNotFoundException)
if (cityServices.TryGetValue("Library", out int books))
Console.WriteLine($"Library: {books} books");
else
Console.WriteLine("Library not found");
Add(key, value) — Adds a new key-value pair; throws if key exists
dict[key] = value — Sets value; adds if not present, overwrites if present
TryGetValue(key, out value) — Safe lookup returning bool; avoids exceptions
ContainsKey(key) — Returns true if the key exists
Remove(key) — Removes the entry by key
Count — Number of key-value pairs
Keys / Values — Collections of all keys or all values
HashSet<T> — Unique Elements Only
A HashSet<T> stores unique elements with O(1) lookups. Duplicates are silently ignored. It also supports set operations like UnionWith, IntersectWith, and ExceptWith.
A Queue<T> processes items in the order they were added — just like a real queue at city hall. Use Enqueue to add and Dequeue to remove from the front.
A Stack<T> is perfect for undo operations. The last action pushed is the first one popped. Think of it as the Architect's undo history.
Stack<string> undoStack = new Stack<string>();
undoStack.Push("Build");
undoStack.Push("Paint");
undoStack.Push("Furnish");
string lastAction = undoStack.Pop(); // "Furnish"
Console.WriteLine($"Undo: {lastAction}");
// Peek at the next undo without removing
string nextUndo = undoStack.Peek(); // "Paint"
Console.WriteLine($"Next undo: {nextUndo}");
When to Use What?
Dictionary — When you need fast key-based lookups (config settings, caches, lookups by ID)
HashSet — When you need guaranteed uniqueness and fast membership checks
Queue — When you process items in arrival order (message queues, BFS, job processing)
Stack — When you need LIFO behavior (undo systems, expression parsing, DFS)
Time to wire up all four collection types for the city's service management system, Architect!
📋 Instructions
**Build the City Services Directory & Task Management System!**
1. Create a `Dictionary` called `services`
2. Add these entries: key `"Fire Station"` → value `"5 trucks"`, key `"Police"` → value `"10 officers"`, key `"Hospital"` → value `"20 beds"`
3. Print `"Fire Station: "` followed by the value for key `"Fire Station"`
4. Print `"Police: "` followed by the value for key `"Police"`
5. Create a `HashSet` called `departments`, add `"Fire"`, `"Police"`, `"Hospital"`, and `"Fire"` again
6. Print `"Has Hospital: "` followed by whether the set contains `"Hospital"`
7. Create a `Queue` called `permits`, enqueue `"Permit A"`, `"Permit B"`
8. Dequeue one item and print `"Queue: "` followed by the dequeued item, then `" processed"`
9. Create a `Stack` called `actions`, push `"Build"` then `"Paint"`
10. Pop the top and print `"Stack: Undo last action: "` followed by the popped value
For Dictionary, use the indexer `services["key"]` to read values. HashSet.Contains() returns a bool. Queue uses Enqueue/Dequeue, Stack uses Push/Pop. Remember that `$"text {variable}"` is string interpolation.