What a set is
A List keeps every value you put in it, repeats too. Sometimes you only care about which values there are. A set is made for that: it holds each value once, and adding a value that is already inside changes nothing. C#'s set is HashSet<T>.
HashSet<string> pets = new HashSet<string>(new[] {"cat", "dog", "cat"}); // cat goes in once
pets.Add("fish"); // a new value
pets.Add("dog"); // already in it, no change
Console.WriteLine(pets.Count); // 3
Console.WriteLine(pets.Contains("dog")); // True
The set starts with two values, cat and dog. Adding fish makes three, and adding dog again leaves it at three.
The main members
new HashSet<string>()makes an empty set of strings.new HashSet<string>(list)builds a set from a list (or an array). Repeats go in once.s.Add(x)putsxin the set. It returnstruewhenxwas new andfalsefor a repeat. In the example above,pets.Add("fish")returnsTrueand the laterpets.Add("dog")returnsFalse.s.Contains(x)istruewhenxis in the set.s.Countis how many values the set holds.
Note that Console.WriteLine prints a bool as True or False, with a capital letter.
Why use a set instead of a list
Checking s.Contains(x) is fast, even for a big set: a set finds a value without looking at all the others. A list's Contains compares the values one by one, so it gets slower as the list grows. That is the reason to reach for a set when you check membership many times.
Two handy uses
Counting the different values in an array takes one line. For {4, 7, 4, 4, 9, 7}, building a set and reading its Count gives 3:
int[] nums = {4, 7, 4, 4, 9, 7};
Console.WriteLine(new HashSet<int>(nums).Count); // 3
And because Add returns false for a repeat, it can spot the first repeat while you loop:
HashSet<string> seen = new HashSet<string>();
foreach (string w in new[] {"a", "b", "a"}) {
if (!seen.Add(w)) {
Console.WriteLine(w + " again"); // a again
}
}
Traps
- The size is
Count, notLength. Arrays and strings haveLength; sets, lists and dictionaries haveCount. - No order and no indexes.
s[0]doesn't compile for a set. If you need the values in a position, use a list. - Strings are compared by their contents.
"Ava"typed twice is one value, even if the two strings were built in different places.
The exercise
The camp's sign-up list names holds Ava, Leo, Ava, Mia, Leo. Build a HashSet named kids from it. Then Zoe signs up, and Ava signs up again: add both names. At the end, print Leo is coming if the set contains "Leo".
- Build the set with
new HashSet<string>(names). Ava and Leo are in the list twice, but each goes in once, so the set holds 3 names. - Call
Add("Zoe")and thenAdd("Ava"). Only Zoe makes the set bigger, so the count becomes 4. - Test
kids.Contains("Leo")and print the message.
class Solution {
public void MainFunction() {
List<string> names = new List<string>(new[] {"Ava", "Leo", "Ava", "Mia", "Leo"});
HashSet<string> kids = new HashSet<string>(names);
Console.WriteLine(kids.Count + " kids");
kids.Add("Zoe");
kids.Add("Ava");
Console.WriteLine(kids.Count + " kids");
if (kids.Contains("Leo")) {
Console.WriteLine("Leo is coming");
}
}
}
Output:
3 kids
4 kids
Leo is coming
Common mistakes in the exercise
- Printing before building the set. The first
Console.WriteLineis already in the editor and readskids.Count, sokidsmust exist above it. - Expecting 5 kids. The list has five entries, but only three different names. The count of the set is 3, and after Zoe joins it is 4: Ava's second sign-up changes nothing.
- Checking with a list.
names.Contains("Leo")would also print the message, but the exercise is about asking the set.
Key takeaways
- A
HashSetholds each value once;Addof a repeat does nothing. - Build one from a list to drop its repeats.
- Use
Containsfor fast checks andCountfor the size. - A set has no order and no indexes.
