Skip to content

Sets

In C#, the most common set type is HashSet<T>. A set stores unique values. If you try to add the same value more than once, the collection keeps only one copy.

This makes sets useful for a different reason than lists or dictionaries.

  • a list focuses on order and position
  • a dictionary focuses on key-to-value lookup
  • a set focuses on uniqueness and membership

Why sets matter

Many programming tasks are really about questions like these:

  • have we already seen this value?
  • is this item selected?
  • which values are unique?

If duplicates are not meaningful, a set can model the problem more directly than a list.

A simple set

csharp
HashSet<string> tags = new() { "csharp", "dotnet" };

tags.Add("collections");
tags.Add("csharp");

Console.WriteLine(tags.Count);

The count does not grow for the second "csharp" because the value was already present.

That is the most important beginner idea about sets: duplicates are ignored.

Membership checks

One of the strongest reasons to use a set is checking whether a value is already present.

csharp
HashSet<int> selectedIds = new() { 3, 7, 10 };

if (selectedIds.Contains(7))
{
    Console.WriteLine("7 is selected.");
}

This reads naturally because the collection is designed around the idea of membership.

Adding and removing values

csharp
HashSet<string> completedSteps = new();

completedSteps.Add("install-sdk");
completedSteps.Add("create-project");
completedSteps.Remove("install-sdk");

The operations are simple, but the meaning is different from list operations. You are not managing positions. You are managing whether values belong to the set.

Set operations

Sets also support operations that match set thinking, such as union and intersection.

csharp
HashSet<int> left = new() { 1, 2, 3 };
HashSet<int> right = new() { 3, 4, 5 };

left.UnionWith(right);

foreach (int value in left)
{
    Console.WriteLine(value);
}

After the union, left contains all unique values from both sets.

This is more advanced than basic add-and-check operations, but it shows why sets are more than "lists with duplicates removed." They represent a different style of thinking about data.

Sets versus lists

Choose a list when:

  • order matters
  • duplicates matter
  • you need indexed access

Choose a set when:

  • uniqueness matters
  • membership checks are common
  • duplicates should not be stored

For example, a music playlist usually belongs in a list because order matters. A collection of allowed role names often belongs in a set because uniqueness matters more than order.

A worked example

csharp
HashSet<string> visitedPages = new();

visitedPages.Add("/home");
visitedPages.Add("/products");
visitedPages.Add("/home");

Console.WriteLine($"Unique pages visited: {visitedPages.Count}");
Console.WriteLine($"Visited /products? {visitedPages.Contains("/products")}");

This is a natural set scenario because visiting the same page twice does not create a new kind of page.

Common mistakes

The biggest mistake is choosing a set when order or duplicates actually matter.

Other common mistakes include:

  • expecting values to remain in a meaningful positional order
  • using a set when a dictionary key-to-value relationship is needed
  • forgetting that duplicate additions are ignored rather than stored again

Summary

HashSet<T> is a good fit when you need:

  • unique values
  • fast membership-style thinking
  • duplicate prevention

It is especially useful for "already seen," "selected," and "allowed values" style problems.

Practice

Create a set of favorite topics, try adding the same topic twice, and print the final count.

As a second exercise, compare a List<string> and a HashSet<string> for storing usernames, then explain which one better matches a uniqueness requirement.

Built with VitePress and published on GitHub Pages.