C# HashSet
A HashSet<T> is a collection that stores unique items only. It automatically rejects duplicates. It also provides blazing-fast lookups — checking whether an item exists is nearly instant regardless of how many items are stored.
What Makes HashSet Special?
┌────────────────────────────────────────────────────────────────┐ │ LIST vs HASHSET │ ├──────────────────────────────┬─────────────────────────────────┤ │ List<T> │ HashSet<T> │ ├──────────────────────────────┼─────────────────────────────────┤ │ Allows duplicates │ No duplicates allowed │ ├──────────────────────────────┼─────────────────────────────────┤ │ Preserves insertion order │ No guaranteed order │ ├──────────────────────────────┼─────────────────────────────────┤ │ Contains() scans all items │ Contains() is near-instant (O1) │ ├──────────────────────────────┼─────────────────────────────────┤ │ General purpose │ Uniqueness and fast lookup │ └──────────────────────────────┴─────────────────────────────────┘
Real-World Analogy
┌────────────────────────────────────────────────────────────┐ │ HashSet = Guest List at an event │ ├────────────────────────────────────────────────────────────┤ │ ✅ "Alice" added → on the list │ │ ✅ "Bob" added → on the list │ │ ❌ "Alice" added → already on list, ignored │ │ │ │ The bouncer (HashSet) never lets the same person in twice │ └────────────────────────────────────────────────────────────┘
Creating a HashSet
using System;
using System.Collections.Generic;
HashSet<int> numbers = new HashSet<int>();
HashSet<string> names = new HashSet<string>() { "Alice", "Bob", "Carol" };
Adding Items
Add() returns true if the item was added, or false if it already existed.
HashSet<string> tags = new HashSet<string>();
Console.WriteLine(tags.Add("csharp")); // True — added
Console.WriteLine(tags.Add("dotnet")); // True — added
Console.WriteLine(tags.Add("csharp")); // False — already exists
Console.WriteLine(tags.Count); // 2
// HashSet contains: { "csharp", "dotnet" }
Add Diagram
┌────────────────────────────────────────────────────────────┐
│ Add("A") → hash("A") → bucket 3 → stored │
│ Add("B") → hash("B") → bucket 7 → stored │
│ Add("A") → hash("A") → bucket 3 → ALREADY EXISTS → skip │
└────────────────────────────────────────────────────────────┘
Checking Membership
HashSet<int> primes = new HashSet<int>() { 2, 3, 5, 7, 11, 13 };
Console.WriteLine(primes.Contains(7)); // True
Console.WriteLine(primes.Contains(6)); // False
// This lookup is O(1) — same speed with 10 or 10 million items
Removing Items
HashSet<string> fruits = new HashSet<string>() { "Apple", "Mango", "Banana" };
fruits.Remove("Mango");
Console.WriteLine(fruits.Contains("Mango")); // False
Console.WriteLine(fruits.Count); // 2
fruits.Clear();
Console.WriteLine(fruits.Count); // 0
Set Operations
HashSet supports mathematical set operations: union, intersection, and difference. These are powerful for comparing two groups of data.
Union — Combine Two Sets
HashSet<int> setA = new HashSet<int>() { 1, 2, 3, 4 };
HashSet<int> setB = new HashSet<int>() { 3, 4, 5, 6 };
setA.UnionWith(setB);
// setA = { 1, 2, 3, 4, 5, 6 } — all unique items from both sets
// Diagram:
// A = { 1, 2, 3, 4 }
// B = { 3, 4, 5, 6 }
// ──────────────────
// A∪B = { 1, 2, 3, 4, 5, 6 }
Intersection — Common Items Only
HashSet<int> setA = new HashSet<int>() { 1, 2, 3, 4 };
HashSet<int> setB = new HashSet<int>() { 3, 4, 5, 6 };
setA.IntersectWith(setB);
// setA = { 3, 4 } — only items in both sets
// Diagram:
// A = { 1, 2, [3, 4] }
// B = { [3, 4], 5, 6 }
// ─────────────────
// A∩B = { 3, 4 }
Difference — Items in A but NOT in B
HashSet<int> setA = new HashSet<int>() { 1, 2, 3, 4 };
HashSet<int> setB = new HashSet<int>() { 3, 4, 5, 6 };
setA.ExceptWith(setB);
// setA = { 1, 2 } — items in A that are NOT in B
// Diagram:
// A = { [1, 2], 3, 4 }
// B = { 3, 4, 5, 6 }
// ─────────────────
// A-B = { 1, 2 }
Symmetric Difference — Items in Either, Not Both
HashSet<int> setA = new HashSet<int>() { 1, 2, 3, 4 };
HashSet<int> setB = new HashSet<int>() { 3, 4, 5, 6 };
setA.SymmetricExceptWith(setB);
// setA = { 1, 2, 5, 6 } — items in one but NOT both
// Diagram:
// A = { [1, 2], 3, 4 }
// B = { 3, 4, [5, 6] }
// shared = removed
// Result = { 1, 2, 5, 6 }
Set Comparison Methods
HashSet<int> a = new HashSet<int>() { 1, 2, 3 };
HashSet<int> b = new HashSet<int>() { 1, 2, 3, 4, 5 };
HashSet<int> c = new HashSet<int>() { 1, 2, 3 };
Console.WriteLine(a.IsSubsetOf(b)); // True — all of a is in b
Console.WriteLine(b.IsSupersetOf(a)); // True — b contains all of a
Console.WriteLine(a.SetEquals(c)); // True — same items
Console.WriteLine(a.Overlaps(b)); // True — at least one common item
Real Example: Remove Duplicates from a List
List<int> withDuplicates = new List<int>() { 5, 3, 8, 3, 1, 5, 9, 8 };
// Convert to HashSet removes all duplicates instantly:
HashSet<int> unique = new HashSet<int>(withDuplicates);
Console.WriteLine(string.Join(", ", unique));
// Output: 5, 3, 8, 1, 9 (order may vary)
// Or convert back to List:
List<int> cleanList = new List<int>(unique);
Iterating Through a HashSet
HashSet<string> cities = new HashSet<string>() { "London", "Tokyo", "Paris" };
foreach (string city in cities)
{
Console.WriteLine(city);
}
// Order is NOT guaranteed
Quick Method Reference
┌──────────────────────────────┬──────────────────────────────────┐ │ Method │ What It Does │ ├──────────────────────────────┼──────────────────────────────────┤ │ Add(item) │ Add item (returns bool) │ │ Remove(item) │ Remove item │ │ Contains(item) │ Fast membership check │ │ Clear() │ Remove all items │ │ Count │ Number of items │ │ UnionWith(other) │ Add all from other set │ │ IntersectWith(other) │ Keep only common items │ │ ExceptWith(other) │ Remove items found in other │ │ SymmetricExceptWith(other) │ Keep non-shared items │ │ IsSubsetOf(other) │ All my items in other? │ │ IsSupersetOf(other) │ All other items in me? │ │ SetEquals(other) │ Exact same items? │ │ Overlaps(other) │ Any items in common? │ └──────────────────────────────┴──────────────────────────────────┘
HashSet is the right collection when uniqueness and fast lookup matter more than order. It powers features like visited-URL tracking in browsers, user tag systems, permission checks, and any scenario where you need to answer "is this item already here?" as fast as possible.
