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