HashSet

A HashSet<T> holds each value once, answers "is it in here?" fast, and has no index or guaranteed order. Add tells you whether a value was new.

Journeyman · C# · Updated

After this lesson you can

  • Use a HashSet<T> to keep values without duplicates, with Add, Contains and Remove
  • Choose between a List<T> and a HashSet<T> for a job

Key takeaways

  • A HashSet<T> holds each value once. Add returns true when the value is new and false when it's already there.
  • Contains tells you fast whether a value is in the set, however big the set grows.
  • A set has no index and no guaranteed order. Loop over it with foreach, and never depend on the order values come out.
  • IntersectWith keeps only the values both sets have, and UnionWith adds everything from the other set.
  • Use a List<T> when order, positions or duplicates matter, and a HashSet<T> when each value should appear once.

A HashSet<T> is a collection that holds each value only once. It's the collection to reach for when the question is "have I seen this before?"

No duplicates

HashSet<string> visitors = new HashSet<string>();

Console.WriteLine(visitors.Add("Ada"));
Console.WriteLine(visitors.Add("Grace"));
Console.WriteLine(visitors.Add("Ada"));

Console.WriteLine(quot;{visitors.Count} different visitors");
Console.WriteLine(visitors.Contains("Grace"));
Console.WriteLine(visitors.Contains("Linus"));
True
True
False
2 different visitors
True
False

Add returns true when the value is new, and false when it's already there; adding it again changes nothing. Contains answers whether a value is in the set, and it answers fast, however big the set grows.

Member Does
Add(value) Adds the value if it's new; returns whether it was added
Contains(value) Whether the value is in the set
Remove(value) Removes the value; returns whether it was there
Count How many values the set holds

No index, no order

A set has no positions: there's no visitors[0]. To see every value, loop over it with foreach. And don't count on the order: a HashSet keeps values in whatever order suits it, not the order you added them, and not sorted.

HashSet<int> seatsSold = new HashSet<int> { 14, 3, 9 };
seatsSold.Add(3);
seatsSold.Remove(9);

Console.WriteLine(quot;{seatsSold.Count} seats sold");
foreach (int seat in seatsSold)
{
    Console.WriteLine(quot;Seat {seat} is taken");
}
2 seats sold
Seat 14 is taken
Seat 3 is taken

(The loop happened to print 14 before 3. Code that depends on a set's order is a bug waiting to happen.)

Two sets together

Sets can be combined. IntersectWith keeps only what both sets have, and UnionWith adds everything from the other set:

HashSet<string> adaKnows = new HashSet<string> { "C#", "SQL", "HTML" };
HashSet<string> jobNeeds = new HashSet<string> { "C#", "SQL", "Angular" };

HashSet<string> matches = new HashSet<string>(adaKnows);
matches.IntersectWith(jobNeeds);
Console.WriteLine(quot;Matching skills: {matches.Count}");
Console.WriteLine(matches.Contains("Angular"));
Matching skills: 2
False

new HashSet<string>(adaKnows) makes a copy first, so adaKnows itself isn't changed.

List or HashSet?

Use a List<T> when Use a HashSet<T> when
The order matters Only "is it in there?" matters
Duplicates are allowed, or meaningful Each value should appear once
You need items by position: list[0] You check Contains often, on many values
Example: the items on an order, in order Example: the seats already sold, the tags on a post

Check yourself

2 questions to try on the lesson page.

So what

When duplicates would be a bug, a HashSet makes them impossible, and it answers "is this one taken?" instantly. Seats, tags, usernames, visited pages: anything that should appear once belongs in a set.

Key terms

Cheat sheets