Mastering Usage of HashSet<T>
Introduction to HashSet<T>
In C#, HashSet<T> is a high-performance collection class designed to store unique elements in an unordered manner. It belongs to the System.Collections.Generic namespace and implements the ISet<T> interface. Unlike List<T> or arrays, HashSet<T> automatically enforces uniqueness—attempting to add a duplicate element has no effect.
Why Use HashSet<T>?
Fast Lookups: Built on a hash table,
HashSet<T>provides O(1) average time complexity for search, insertion, and deletion.Memory Efficiency: Optimized to handle large datasets efficiently.
Set Operations: Supports mathematical set operations like Union, Intersection, and Difference.
No Duplicates: Ensures all elements are unique without manual checks.
Key Features of HashSet<T>
| Feature | Description |
|---|---|
| Uniqueness | Automatically rejects duplicate values. |
| Unordered Storage | Elements are not stored in any particular order. |
| Fast Operations | Add(), Remove(), and Contains() run in O(1) time on average. |
| Set Operations | Supports UnionWith(), IntersectWith(), and ExceptWith(). |
| Custom Equality | Allows defining custom equality logic via IEqualityComparer<T>. |
Basic Operations with HashSet<T>
1. Creating and Initializing a HashSet
2. Adding and Removing Elements
3. Checking for Existence
4. Counting Elements
5. Iterating Over Elements
Output:
Advanced HashSet<T> Operations
1. Set Operations (Union, Intersection, Difference)
Union (UnionWith)
Combines two sets, keeping only unique elements.
Intersection (IntersectWith)
Keeps only elements present in both sets.
Difference (ExceptWith)
Removes elements that exist in another set.
2. Custom Equality Comparison
By default, HashSet<T> uses Equals() and GetHashCode(). You can override these or use a custom IEqualityComparer<T>.
Example: Case-Insensitive String HashSet
Example: Custom Object Comparison
Performance Considerations
| Operation | Time Complexity | Notes |
|---|---|---|
Add() | O(1) | Fast, but may degrade if many hash collisions occur. |
Remove() | O(1) | Efficient deletion. |
Contains() | O(1) | Best for frequent lookups. |
UnionWith() | O(N) | Depends on the size of the other set. |
When to Use HashSet<T>?
✔ Removing duplicates from a list.
✔ Checking membership (e.g., "Is this username taken?").
✔ Mathematical set operations (union, intersection).
When NOT to Use HashSet<T>?
❌ Order matters (use List<T> or SortedSet<T> instead).
❌ Frequent indexed access (no HashSet[index]).
Practical Example: Removing Duplicates from a List
Output: [1, 2, 3, 4, 5]
Thread Safety & Alternatives
HashSet<T> is not thread-safe. For concurrent scenarios:
Use
ConcurrentDictionary<TKey, TValue>(if key-value pairs are needed).Use
lockfor manual synchronization.
Conclusion
HashSet<T> is a powerful collection for storing unique elements with fast lookups and set operations. It excels in:
✅ Ensuring uniqueness without manual checks.
✅ Optimizing search operations (O(1) time).
✅ Performing set-based logic (union, intersection).
By mastering HashSet<T>, you can write more efficient and cleaner C# code when dealing with unique data.
