C# Dictionaries and Sets
Look up values by meaningful keys, track unique membership, and choose between ordered sequences, mappings, and sets.
Before this lesson
- Arrays and List<T>
- Loops
- Try-style APIs
Add, update, and safely retrieve dictionary entries
Use a set for uniqueness and membership
Select a collection from the questions the program asks most often
The short answer
Dictionary<TKey,TValue> maps each unique key to a value for efficient lookup. HashSet<T> stores unique values and answers membership questions. Use List<T> when order and duplicates are the main concern.
Sometimes a position is the wrong identity
A list can answer ‘what is item 3?’ but a stock system usually asks ‘how many notebooks are available?’ Scanning every product each time is indirect. A dictionary attaches a meaningful unique key to each value so the lookup states the real question.
TKey and TValue are generic type parameters. Dictionary<string, int> uses text keys and integer values. Keys must be unique; assigning through the indexer updates the value for an existing key, while Add rejects a duplicate key.
Retrieve uncertain keys without an exception
Reading prices["marker"] throws KeyNotFoundException when marker is absent. That is fine only when absence proves a broken internal assumption. When a user or file supplies the key, absence is normal and should be a branch.
TryGetValue performs one lookup and returns both a success flag and the value. ContainsKey followed by the indexer can work but performs the search twice. Choose the API that models the operation directly.
Dictionary<string, decimal> prices = new Dictionary<string, decimal>();
prices["tea"] = 2.50m;
prices["coffee"] = 3.25m;
decimal price;
if (prices.TryGetValue("juice", out price))
{
Console.WriteLine(price);
}
else
{
Console.WriteLine("Price not found");
}Expected output
Price not found
A set answers whether a value belongs
HashSet<T> stores each equal value at most once. Add returns false when the value was already present. Sets suit tags, visited identifiers, blocked codes, and removing duplicates when original order is not the primary requirement.
Set operations express larger questions: UnionWith adds values from another set, IntersectWith keeps shared values, and ExceptWith removes values found in another set. These operations can be clearer than nested loops when the domain really is membership.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<string> attendees = new HashSet<string>();
Console.WriteLine(attendees.Add("Amina"));
Console.WriteLine(attendees.Add("Bilal"));
Console.WriteLine(attendees.Add("Amina"));
Console.WriteLine(attendees.Count);
}
}Expected output
True True False 2
Choose by the dominant operation
A List is an ordered sequence and allows duplicates. A Dictionary maps unique keys to values. A HashSet represents unique membership. The same data can be forced into any of them, but the wrong choice makes the main operation slow or unclear.
Default string equality is case-sensitive. If product codes or usernames should ignore case, provide an appropriate comparer such as StringComparer.OrdinalIgnoreCase when constructing the dictionary or set. Choosing comparison rules at construction keeps every operation consistent.
| Question | Collection | Example |
|---|---|---|
| What is at this position? | List<T> or array | Daily temperatures |
| What value belongs to this key? | Dictionary<TKey,TValue> | Price by product code |
| Have I seen this value? | HashSet<T> | Processed order IDs |
| Can duplicates appear in order? | List<T> | Page visit history |
Good habits
- Use stable, meaningful keys; mutable objects make dangerous dictionary keys.
- Do not depend on hash collection ordering unless the documented type contract guarantees what you need.
- Choose an equality comparer that matches the domain at collection creation.
Quick knowledge check
Answer before you reveal.
01Can a Dictionary contain the same key twice?
No. Each key is unique, though multiple keys may map to equal values.
02Why use TryGetValue instead of ContainsKey followed by the indexer?
It retrieves the value and reports absence with one lookup and one clear operation.
Practice challenge
Now build it without copying.
Count votes from a string array using Dictionary<string, int>. Treat candidate names case-insensitively, print each total, and use a HashSet to report the number of unique candidates.
You are done when
- Repeated names increase an existing count
- Different capitalization does not create another candidate
- Missing keys are handled before incrementing
Stretch: Track and print the leading candidate, and state how you handle a tie.
Open challenge in playgroundLesson checkpoint
One small step locks it in
Mark this lesson complete, then keep the momentum going.
Clear up the details
Frequently asked questions
Are dictionary lookups always constant time?
Hash-based lookup is constant-time on average with suitable hashing, not an absolute guarantee for every input and operation.
How do I keep dictionary entries sorted?
Use an ordered or sorted collection when sorted keys are a real requirement, or sort entries for presentation. Dictionary itself should not be selected for a display-order promise.
Can a set store objects?
Yes, but uniqueness depends on equality and hash-code behavior. Records or types with well-designed equality are often better set elements than mutable identity objects.