Collections & LINQ · Lesson 16

Hash sets for membership checks

Choose a set when the question is whether an item is already present.

How it works

A HashSet stores unique values according to an equality comparer. It is a useful tool when the main operation is membership: has this identifier already been seen, or is this code in the allowed set? Adding a duplicate does not append another copy. The return value of Add reports whether the set changed, which can remove the need for a separate membership check before insertion.

Equality is part of the data model. Two differently capitalized identifiers may represent one logical value in a case-insensitive format, while display names may require different rules. Supply a comparer at construction so every subsequent Add, Contains, and Remove operation uses the same contract. A set should not be used as a substitute for a list when duplicate occurrences or their original positions are meaningful.

Walk through the example. The first two additions use differently capitalized versions of the same code. With OrdinalIgnoreCase, the first succeeds and the second reports false. Adding another code raises the unique count to two. Contains uses the same comparer and therefore finds a mixed-case query. Sorting only for display makes the final output deterministic without depending on the set's enumeration order.

Watch the boundary. A hash set does not promise a sorted presentation order. Even when a small example appears to retain insertion order, do not make that observation a format contract. Mutable keys also require care: changing a field that participates in equality or hashing after insertion can make membership operations behave incorrectly. Prefer stable identifier values for keys.

Practice independently. Build a duplicate detector for a batch of product codes. Preserve the first accepted spelling in a list for display, while using a case-insensitive set to decide whether each code is new. Test exact duplicates, case variants, an empty batch, and a blank-code policy. Report both the number of input records and the number of distinct logical identifiers.

A working example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(seen.Add("A1"));
Console.WriteLine(seen.Add("a1"));
seen.Add("B2");
Console.WriteLine(seen.Count);
Console.WriteLine(seen.Contains("b2"));
Console.WriteLine(string.Join(",", seen.OrderBy(x => x, StringComparer.Ordinal)));

Expected output

True
False
2
True
A1,B2
Try this
Use Add’s return value for duplicate detection and sort separately for presentation.