HashSet
List
Intersection
C#
Algorithms

Fast intersection of HashSetint and Listint

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The fastest way to intersect a HashSet<int> and a List<int> depends on what you want the result to mean. If you want unique results, use HashSet.IntersectWith. If you want to preserve duplicates from the list, iterate the list and test membership with the hash set's Contains.

Start With The Meaning Of "Intersection"

A HashSet<int> contains unique values. A List<int> can contain duplicates and preserves order.

That means there are two reasonable interpretations:

  • set-style intersection, where each output value appears once
  • list-filter intersection, where duplicates from the list are preserved

The implementation changes depending on which behavior you need.

Fast Unique Intersection

If you want unique output values, the cleanest solution is to clone the hash set and intersect it with the list.

csharp
1using System;
2using System.Collections.Generic;
3
4var set = new HashSet<int> { 1, 2, 3, 4, 5 };
5var list = new List<int> { 3, 3, 4, 6, 7 };
6
7var result = new HashSet<int>(set);
8result.IntersectWith(list);
9
10foreach (var value in result)
11{
12    Console.WriteLine(value);
13}

Why this is fast:

  • membership checks in HashSet<int> are average O(1)
  • 'IntersectWith is optimized for set operations'
  • the output stays unique naturally

For set semantics, this is usually the best answer.

Fast Intersection That Preserves List Duplicates

If you want every matching item from the list, including repeated values, just filter the list using the set:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5var set = new HashSet<int> { 1, 2, 3, 4, 5 };
6var list = new List<int> { 3, 3, 4, 6, 7 };
7
8var result = list.Where(set.Contains).ToList();
9
10Console.WriteLine(string.Join(", ", result));

This returns 3, 3, 4.

That is still efficient because each lookup into the hash set is average O(1), so the total work is linear in the size of the list.

Complexity Discussion

For the duplicate-preserving version:

  • iterate the list once
  • do one hash lookup per item

That makes the time complexity approximately O(n) where n is the size of the list, assuming the hash set already exists.

For the unique set-style version with IntersectWith, the total cost is still effectively linear in the sizes of the collections involved.

The expensive approach is the wrong one:

csharp
var result = list.Where(x => list.Contains(x)).ToList();

or anything that repeatedly calls List.Contains against another list. That pushes lookups toward O(n) each and can easily turn the total work into quadratic behavior.

Choose The Smaller Collection To Hash If Needed

If neither side is already a HashSet<int>, a good general rule is to hash the smaller collection and iterate the larger one. That minimizes memory overhead while keeping membership tests fast.

Example:

csharp
1var first = new List<int> { 1, 2, 3, 4, 5 };
2var second = new List<int> { 3, 4, 5, 6, 7, 8 };
3
4var lookup = new HashSet<int>(first.Count <= second.Count ? first : second);
5var scan = first.Count <= second.Count ? second : first;
6
7var result = scan.Where(lookup.Contains).Distinct().ToList();

That pattern is useful when the inputs are general collections rather than one set and one list.

Be Careful With LINQ Semantics

LINQ makes the code concise, but it does not change the underlying rules. Intersect returns unique results because it uses set semantics:

csharp
var result = list.Intersect(set).ToList();

That result would contain 3, 4, not 3, 3, 4.

So Intersect is fine when uniqueness is what you want, but it is the wrong answer if list duplicates must survive.

Common Pitfalls

The biggest mistake is not deciding whether duplicates should be preserved. That semantic choice determines the correct implementation.

Another mistake is using List.Contains repeatedly when a hash lookup is available. That can turn a simple task into a slow quadratic scan.

People also forget that LINQ Intersect removes duplicates. It is a set operation, not a duplicate-preserving filter.

Finally, do not mutate the original hash set with IntersectWith unless that is intentional. Clone it first if the original set must remain unchanged.

Summary

  • Use HashSet.IntersectWith for a fast unique intersection.
  • Use list.Where(set.Contains) when you need to preserve duplicates from the list.
  • Hash lookups are average O(1), which keeps these solutions efficient.
  • 'Enumerable.Intersect uses set semantics and removes duplicates.'
  • Decide the output semantics first, then choose the implementation that matches them.

Course illustration
Course illustration

All Rights Reserved.