.NET
C#
programming
data structures
HashSet

Convert an array to a HashSetT in .NET

Master System Design with Codemia

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

Introduction

Converting an array to a HashSet<T> in .NET removes duplicates and enables O(1) average-time lookups, adds, and removals. The simplest approach is passing the array directly to the HashSet<T> constructor. You can also use LINQ's ToHashSet() extension method (available since .NET Core 2.0 / .NET Framework 4.7.2). Both approaches produce the same result — a set containing the unique elements from the array.

Constructor Approach

csharp
1string[] names = { "Alice", "Bob", "Alice", "Charlie", "Bob" };
2HashSet<string> nameSet = new HashSet<string>(names);
3
4Console.WriteLine(nameSet.Count);  // 3 — duplicates removed
5// nameSet contains: "Alice", "Bob", "Charlie"

The HashSet<T> constructor accepts any IEnumerable<T>, so arrays, lists, and other collections all work.

ToHashSet() Extension Method

csharp
1using System.Linq;
2
3int[] numbers = { 1, 2, 3, 2, 1, 4 };
4HashSet<int> numberSet = numbers.ToHashSet();
5
6Console.WriteLine(numberSet.Count);  // 4

ToHashSet() is often combined with LINQ queries to convert filtered or transformed results into a set:

csharp
1string[] words = { "Hello", "HELLO", "hello", "World" };
2
3// Case-insensitive set
4HashSet<string> uniqueWords = words.ToHashSet(StringComparer.OrdinalIgnoreCase);
5Console.WriteLine(uniqueWords.Count);  // 2 — "Hello" and "World"

Custom Equality Comparer

By default, HashSet<T> uses EqualityComparer<T>.Default, which calls GetHashCode() and Equals(). For custom types, you may need to provide your own comparer:

csharp
1public class User
2{
3    public int Id { get; set; }
4    public string Name { get; set; }
5}
6
7public class UserIdComparer : IEqualityComparer<User>
8{
9    public bool Equals(User x, User y) => x?.Id == y?.Id;
10    public int GetHashCode(User obj) => obj.Id.GetHashCode();
11}
12
13User[] users =
14{
15    new User { Id = 1, Name = "Alice" },
16    new User { Id = 2, Name = "Bob" },
17    new User { Id = 1, Name = "Alice Copy" }
18};
19
20// Without comparer: all 3 are considered unique (reference equality)
21HashSet<User> defaultSet = new HashSet<User>(users);
22Console.WriteLine(defaultSet.Count);  // 3
23
24// With comparer: Id 1 appears once
25HashSet<User> idSet = new HashSet<User>(users, new UserIdComparer());
26Console.WriteLine(idSet.Count);  // 2

Set Operations

Once you have a HashSet<T>, you can perform efficient set operations:

csharp
1int[] arrayA = { 1, 2, 3, 4, 5 };
2int[] arrayB = { 3, 4, 5, 6, 7 };
3
4HashSet<int> setA = new HashSet<int>(arrayA);
5HashSet<int> setB = new HashSet<int>(arrayB);
6
7// Intersection: elements in both sets
8setA.IntersectWith(setB);
9// setA: { 3, 4, 5 }
10
11// Reset
12setA = new HashSet<int>(arrayA);
13
14// Union: elements in either set
15setA.UnionWith(setB);
16// setA: { 1, 2, 3, 4, 5, 6, 7 }
17
18// Reset
19setA = new HashSet<int>(arrayA);
20
21// Difference: elements in A but not B
22setA.ExceptWith(setB);
23// setA: { 1, 2 }
24
25// Symmetric difference: elements in either but not both
26setA = new HashSet<int>(arrayA);
27setA.SymmetricExceptWith(setB);
28// setA: { 1, 2, 6, 7 }

Performance Comparison

OperationArrayHashSet
Contains (lookup)O(n)O(1) average
AddO(n) if resizingO(1) average
RemoveO(n)O(1) average
IterationO(n)O(n)
MemoryContiguous, compactHash buckets, higher overhead
Preserves orderYesNo (unordered)
Allows duplicatesYesNo

Use HashSet<T> when you need fast membership tests or deduplication. Use arrays when you need indexed access, ordering, or minimal memory footprint.

Converting Back to Array

csharp
1HashSet<int> set = new HashSet<int> { 1, 2, 3 };
2int[] array = set.ToArray();
3// or
4int[] array2 = new int[set.Count];
5set.CopyTo(array2);

Common Pitfalls

  • Assuming order is preserved: HashSet<T> does not guarantee insertion order. If you need ordered unique elements, use LinkedHashSet (not built-in) or SortedSet<T> (sorted order).
  • Forgetting to override GetHashCode and Equals: For custom classes, the default HashSet<T> uses reference equality. Two different objects with the same data are treated as distinct. Implement IEqualityComparer<T> or override both methods on the class.
  • Mutating objects after adding to HashSet: If you change a property that affects GetHashCode() after inserting the object, the set cannot find it anymore. The hash bucket is wrong, causing Contains to return false and duplicates to be added.
  • Using ToHashSet() on older .NET Framework versions: ToHashSet() was added in .NET Framework 4.7.2 and .NET Core 2.0. On older versions, use the constructor: new HashSet<T>(array).
  • Thread safety: HashSet<T> is not thread-safe. For concurrent access, use ConcurrentDictionary<T, byte> as a workaround or protect access with a lock.

Summary

  • Pass an array to new HashSet<T>(array) or call array.ToHashSet() to convert
  • Duplicates are automatically removed during conversion
  • Provide an IEqualityComparer<T> for custom equality logic or case-insensitive comparisons
  • HashSet<T> provides O(1) lookups, adds, and removes — much faster than searching an array
  • Use set operations (IntersectWith, UnionWith, ExceptWith) for efficient collection comparisons
  • Order is not preserved — use SortedSet<T> if you need sorted output

Course illustration
Course illustration

All Rights Reserved.