Dictionary
Dictionary<TKey,TValue> provides O(1) average hash-based lookups — essential for caches, indexes, and grouping.
Introduction
Dictionary<TKey,TValue> provides O(1) average hash-based lookups — essential for caches, indexes, and grouping. Keys must be unique with consistent GetHashCode and Equals implementations.
ConcurrentDictionary handles multi-threaded scenarios. Interviewers ask hash collision handling, why mutable keys are dangerous, and Dictionary vs Lookup vs FrozenDictionary (.NET 8).
The story
A currency exchange desk at an airport caches FX rates in memory so tellers get instant quotes. Each rate is keyed by a currency pair like USD→EUR; lookups must be fast and duplicate keys must never silently overwrite without intent.
Dictionary<TKey, TValue> with a well-defined key type is the standard pattern for in-memory caches in trading and banking apps.
Understanding the topic
Key concepts
- Key-value pairs with unique keys.
- TryGetValue avoids double lookup.
- Custom keys need proper Equals/GetHashCode override.
- Order undefined — use SortedDictionary for order.
- FrozenDictionary immutable optimized read (.NET 8).
- Collection initializer: { ['a'] = 1 }.
Step-by-step explanation
- Hash code buckets entries; collision chains or open addressing.
- Add throws on duplicate key; TryAdd for idempotent.
- Remove by key; Clear empties.
- Keys, Values collections are views.
- Enumerate KeyValuePair
. - EqualityComparer
.Default for primitives/strings.
Practical code example
In-memory currency rate cache with TryGetValue and record key:
namespace TechLearningPro.Dictionary;public readonly record struct CurrencyPair(string From, string To);public sealed class FxRateCache{private readonly Dictionary<CurrencyPair, decimal> _rates = new();public bool TryGetRate(string from, string to, out decimal rate){var key = new CurrencyPair(from.ToUpperInvariant(), to.ToUpperInvariant());return _rates.TryGetValue(key, out rate);}public void SetRate(string from, string to, decimal rate) =>_rates[new CurrencyPair(from.ToUpperInvariant(), to.ToUpperInvariant())] = rate;}
Line-by-line code explanation
readonly record struct CurrencyPair(string From, string To)is an immutable value-type key with built-in equality.Dictionary<CurrencyPair, decimal> _ratesmaps pairs to exchange rates.TryGetRate(string from, string to, out decimal rate)follows the Try-pattern for safe lookups.new CurrencyPair(from.ToUpperInvariant(), to.ToUpperInvariant())normalizes casing before keying._rates.TryGetValue(key, out rate)performs a single hash lookup — no double search.return _rates.TryGetValue(...)propagates whether the rate was found.SetRate(...)inserts or updates a rate for a currency pair._rates[...] = rateuses the indexer to assign the value for the computed key.
Key takeaway: record struct CurrencyPair gets value-based equality for dictionary keys. TryGetValue is idiomatic — no double lookup.
Real-world use
Where you'll use this in production
- Session stores mapping userId to session data.
- Configuration key lookup.
- Grouping counts in analytics dashboards.
- Memoization caches in algorithms.
Best practices
- Always TryGetValue instead of ContainsKey + indexer.
- Use immutable record keys.
- Consider FrozenDictionary for static read-heavy maps (.NET 8).
- ConcurrentDictionary for parallel updates.
- Do not mutate key objects after insertion.
Common mistakes
- Custom class key without GetHashCode override.
- Assuming enumeration order stable across runs.
- Using float keys — precision breaks equality.
- Locking entire Dictionary under contention — use concurrent type.
Advanced interview questions
Q1BeginnerDictionary time complexity?
Q2BeginnerTryGetValue vs ContainsKey?
Q3IntermediateWhy immutable keys?
Q4IntermediateDictionary vs Lookup?
Q5AdvancedImplement LRU cache with Dictionary.
Summary
Dictionary provides fast key-value lookups. Use TryGetValue and immutable keys. FrozenDictionary and ConcurrentDictionary for specialized needs. Override equality correctly for custom keys. Next: HashSet for unique unordered sets.