Dictionaries & HashSets in C# – Dictionary<TKey, TValue>, HashSet<T>
Introduction
In C#, when working with collections of data where uniqueness or fast lookup is important, Dictionary<TKey, TValue> and HashSet<T> are two of the most powerful and efficient data structures available. Both are implemented using hashing algorithms and provide fast access times, but they serve different purposes.
Dictionary<TKey, TValue> is a key-value pair collection that allows you to store and retrieve values based on a unique key, while HashSet<T> is a collection designed to store unique elements only—no duplicates allowed.
This guide explores both in depth, compares their syntax and performance, and explains when to use each effectively.
What is Dictionary<TKey, TValue>?
Dictionary<TKey, TValue> is a generic collection in the System.Collections.Generic namespace that stores data in a key-value format. It allows quick lookups, additions, and deletions using a unique key.
- Fast Lookup: Retrieves values in constant time on average.
- Unique Keys: Each key must be unique within the dictionary.
- Type Safety: Strongly typed to prevent key/value mismatches.
Dictionary<int, string> users = new Dictionary<int, string>();
users.Add(1, "Alice");
users.Add(2, "Bob");
Console.WriteLine(users[1]); // Output: Alice
What is HashSet<T>?
HashSet<T> is a collection that stores only unique elements. It is part of the System.Collections.Generic namespace and provides high-performance set operations such as union, intersection, and difference.
- No Duplicates: Automatically ignores repeated elements.
- Optimized Lookup: Offers quick add, remove, and search operations.
- Mathematical Sets: Useful for operations like union and intersection.
HashSet<string> tags = new HashSet<string>();
tags.Add("csharp");
tags.Add("dotnet");
tags.Add("csharp"); // Duplicate, will not be added
Console.WriteLine(tags.Count); // Output: 2
Dictionary vs HashSet: Core Differences
Though both Dictionary and HashSet are based on hash tables and provide fast access to data, their usage and data models differ significantly. Here's a side-by-side comparison:
| Feature | Dictionary<TKey, TValue> | HashSet<T> |
|---|---|---|
| Data Structure | Key-Value Pairs | Single Values (Set) |
| Uniqueness | Keys must be unique | All elements must be unique |
| Access Method | Via key (e.g., users[1]) | Via methods (e.g., Contains) |
| Primary Use | Lookup and mapping relationships | Presence-checking, sets |
| Duplicates | Values can repeat, keys cannot | No duplicates allowed |
Performance Comparison
Both Dictionary and HashSet offer constant-time operations on average—thanks to their use of hashing. However, the actual speed can depend on factors like hash collisions, load factor, and type complexity.
- Add, Remove, Lookup: O(1) on average for both.
- Memory Usage: Dictionary may consume more due to storing key-value pairs.
- Iteration: Dictionary iterates over key-value pairs; HashSet over single elements.
Here's a sample to show how both perform basic tasks:
// Dictionary
var students = new Dictionary<int, string>();
students[101] = "Ava";
Console.WriteLine(students.ContainsKey(101)); // True
// HashSet
var studentIDs = new HashSet<int>();
studentIDs.Add(101);
Console.WriteLine(studentIDs.Contains(101)); // True
Use Cases for Dictionary<TKey, TValue>
Dictionaries are the best choice when you need to associate keys with values or perform fast key-based lookups. Typical scenarios include:
- Mapping user IDs to usernames
- Caching data by unique keys
- Storing settings or configuration values
- Counting frequency of items
Example: Count Word Frequency
string[] words = { "apple", "banana", "apple", "orange" };
Dictionary<string, int> wordCount = new Dictionary<string, int>();
foreach (var word in words)
{
if (wordCount.ContainsKey(word))
wordCount[word]++;
else
wordCount[word] = 1;
}
Use Cases for HashSet<T>
HashSet<T> excels when you need fast membership testing and set operations. It's ideal for:
- Checking if an item already exists
- Removing duplicates from a list
- Performing unions and intersections
- Tracking visited states or items
Example: Remove Duplicates
string[] names = { "Alice", "Bob", "Alice", "Charlie" };
HashSet<string> uniqueNames = new HashSet<string>(names);
foreach (var name in uniqueNames)
{
Console.WriteLine(name);
}
Advanced Set Operations with HashSet<T>
HashSet<T> offers built-in methods to handle set-based logic efficiently—just like in mathematical set theory. These operations are highly optimized for performance.
UnionWith()– Combines two sets, removing duplicatesIntersectWith()– Keeps only elements found in both setsExceptWith()– Removes all elements that exist in another set
Example: Using IntersectWith
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4 };
HashSet<int> set2 = new HashSet<int> { 3, 4, 5, 6 };
set1.IntersectWith(set2); // set1 now contains { 3, 4 }
Best Practices
- Always check for key existence using
ContainsKey()before accessing Dictionary values. - Use HashSet when uniqueness is key, and you don’t care about order or indexing.
- Use Dictionary when mapping is necessary, not just presence checking.
- Leverage LINQ with HashSet and Dictionary for more expressive code.
Conclusion
Dictionary<TKey, TValue> and HashSet<T> are foundational to efficient C# programming. Each has a purpose—Dictionaries are perfect for mapping and fast retrieval by key, while HashSets are unbeatable for ensuring uniqueness and quick membership tests.
In short:
- Use Dictionary when you need key-value storage.
- Use HashSet when you need unique elements with quick access.
Mastering these structures will help you write cleaner, faster, and more maintainable code.
FAQs
1. Can I use a custom class as a key in a Dictionary?
Yes, but you must override Equals() and GetHashCode() methods to ensure correct behavior.
2. Does HashSet preserve order?
No. If you need order and uniqueness, consider using SortedSet<T> or List<T> with manual filtering.
3. Can I store duplicate values in a Dictionary?
Yes, but only the keys must be unique. You can have duplicate values mapped to different keys.
4. Which one is thread-safe?
Neither is thread-safe by default. Use ConcurrentDictionary or locks for multi-threaded scenarios.
5. Can I convert a List to a HashSet?
Yes, simply pass the list into the HashSet constructor: new HashSet<T>(myList);

Post a Comment