LogIn
I don't have account.

HashSet Methods in C#

DevSniper

169 Views

The HashSet<T> class in C# is part of the System.Collections.Generic namespace and represents a collection of unique elements. It’s ideal for scenarios where you need fast lookups, no duplicates and efficient set operations like union, intersection and difference.

In this article, we’ll explore all important methods of HashSet<T> with syntax, detailed explanations, real-world examples and time complexity analysis.

What is a HashSet in C#?

A HashSet<T> in C# is a generic collection designed for unique elements only. It prevents duplicates and offers lightning-fast operations like addition, removal and lookup (typically O(1) average time). Unlike a List<T>, it does not preserve element order, so it’s ideal when you care about membership, not sequence. Explore HashSet in depth

HashSet Methods in C#

1. Add

public bool Add(T item);

Adds the specified element to the HashSet if it is not already present. Returns true if the element was added successfully, otherwise false.

Copy
var set = new HashSet<string>();
set.Add("C#");
set.Add("Java");
set.Add("C#"); // Duplicate ignored

foreach (var item in set)
    Console.WriteLine(item);
C#
Java
  • Time Complexity: O(1) average , worst case O(n) (when resizing or many collisions)
  • Space Complexity: O(n) where n is number of elements stored

2. Contains()

public bool Contains(T item);

The Contains method checks whether the specified element exists in the HashSet<T>. It returns true if the item is found in the set, otherwise false. Internally, it computes the hash code of the item, locates the corresponding bucket and then traverses any linked entries (in case of collisions) to check equality

Copy
var set = new HashSet<int> { 1, 2, 3 };

Console.WriteLine(set.Contains(2)); // True
Console.WriteLine(set.Contains(5)); // False
  • Time Complexity : Average time O(1) (constant time) due to hash-based indexing and Worst case O(n) , when many elements collide into the same bucket (poor distribution) or capacity is fully loaded.
  • Space Complexity : O(1) auxiliary (since no new collection is created), though overall structure uses O(n) space.
  • Use Contains() when you need fast membership checks in a collection of unique elements without explicitly iterating over the entire collection.

3. Clear()

public void Clear();

The Clear() method removes all elements from the HashSet<T>. After calling it, the set is empty (Count becomes 0) and ready for reuse. Reference: The Clear() method is listed in the documentation for HashSet<T> under methods.

Copy
var set = new HashSet<string> { "A", "B", "C" };
Console.WriteLine(set.Count); // 3

set.Clear();
Console.WriteLine(set.Count); // 0
  • Time Complexity : O(n) , Since it must remove (or mark as removed) each element currently stored.
  • Space Complexity : O(1) additional space (aside from internal set storage) though the internal buffers remain allocated until explicitly trimmed.
  • Use Clear() when you want to reset a HashSet<T> to its empty state for example
    • Re-using the same set instance in a loop or process rather than creating a new one each time.
    • Clearing buffered or temporary data between runs without reallocating new data structures.
    • Preparing the set for a new collection of elements after finishing one phase of work.
  • After Clear() the internal storage capacity remains unchanged: the buckets and entries arrays remain allocated to the prior size. If you anticipate the set will shrink and you want to reclaim memory, you can call TrimExcess() or reconstruct a new set.

4. CopyTo

public void CopyTo(T[] array);
public void CopyTo(T[] array, int arrayIndex);
public void CopyTo(T[] array, int arrayIndex, int count);

The CopyTo() method copies the elements of a HashSet<T> collection into a one-dimensional array. There are three overloaded methods

  1. CopyTo(T[] array) :- copies all elements into the destination array starting at index 0.
  2. CopyTo(T[] array, int arrayIndex) :- copies all elements starting at the specified index of the destination array.
  3. CopyTo(T[] array, int arrayIndex, int count) :- copies the specified number of elements (count) starting at arrayIndex in the destination array.

If the destination array is null, or arrayIndex is out of range or there is insufficient space from arrayIndex to the end of the destination array, exceptions will be thrown.

Copy
using System.Collections.Generic;
using System;

class HelloWorld
{
    static void Main()
    {
        var set = new HashSet<int> { 10, 20, 30 };
        int[] arr1 = new int[3];
        set.CopyTo(arr1);
        Console.WriteLine("--> arr1 :- " + string.Join(", ", arr1));
        int[] arr2 = new int[5];
        set.CopyTo(arr2, 1);
        Console.WriteLine("--> arr2 :- "+string.Join(", ", arr2));
        int[] arr3 = new int[5];
        set.CopyTo(arr3, 1, 2);
        Console.WriteLine("--> arr3 :- "+string.Join(", ", arr3));
    }
}
--> arr1 :- 10, 20, 30
--> arr2 :- 0, 10, 20, 30, 0
--> arr3 :- 0, 10, 20, 0, 0
  • Time Complexity : O(n), where n is the number of elements being copied (typically the Count of the set or the count parameter).
  • Space Complexity : O(1), additional (the destination array must already exist, no new collection is created by this method).
  • Use CopyTo() when you want to convert a HashSet<T> into an array for further processing.
  • Because HashSet<T> does not guarantee element order, the order of elements in the destination array will reflect the enumeration order of the set, not necessarily insertion order or sorted order.
  • Be sure the destination array has sufficient length from arrayIndex to accommodate the number of elements copied, especially when using the count overload. Failing to do so triggers an ArgumentException.
  • If you only need an array of all elements and ordering is not important, you may use set.ToArray() (LINQ) instead.

Note :- Order is not guaranteed, it means HashSet<T> is an unordered collection, so the order in which elements are copied depends on the internal hash organization, not on insertion order.

5. CreateSetComparer()

public static IEqualityComparer<HashSet<T>> CreateSetComparer();

The CreateSetComparer() method returns an IEqualityComparer<HashSet<T>> instance that allows you to compare two HashSet<T> objects based on their contents, rather than by reference or order.

This comparer checks whether two sets contain the same elements, regardless of order making it ideal for comparing sets stored inside other collections (like dictionaries, lists or other hash sets).

In short : It enables value-based equality between sets, not just memory-based equality.

Copy
var comparer = HashSet<int>.CreateSetComparer();

var set1 = new HashSet<int> { 1, 2, 3 };
var set2 = new HashSet<int> { 3, 2, 1 };

Console.WriteLine(comparer.Equals(set1, set2));
// True — both sets contain the same elements

Learn CreateSetComparer in depth covering all aspects

6. EnsureCapacity()

public int EnsureCapacity(int capacity);

The EnsureCapacity() method ensures that a HashSet<T> instance can hold at least the specified number of elements without triggering internal resizing.

When you know in advance how many elements will be added to the set, calling EnsureCapacity() helps optimize performance by reducing the number of memory reallocations and hash re-computations that occur as the collection grows.

It pre-allocates enough space so your HashSet doesn’t need to resize multiple times as you add items.

Copy
var set = new HashSet<int>();
// Allocate capacity for 100 elements in advance
set.EnsureCapacity(100);
for (int i = 0; i < 100; i++)
{
    set.Add(i);
}
Console.WriteLine($"Count: {set.Count}");
// Output: Count: 100

By default, HashSet<T> automatically expands its internal storage (buckets and entries) as you add elements. However, each resize involves:

  • Allocating a new array of buckets
  • Recomputing hash codes
  • Re-inserting all existing elements

This can introduce unnecessary overhead if you already know your expected size in advance ,especially in large data sets or performance-critical applications.

Returns the current capacity of the HashSet<T> after ensuring the specified capacity. If the current capacity is already greater than or equal to the requested one, no resizing occurs.

In short : EnsureCapacity() is a simple but powerful performance optimization tool that ensures your HashSet can handle large, predictable workloads efficiently without the hidden cost of multiple resizes.

Scenario Operation Description Time Complexity Space Complexity Explanation
Best Case Requested capacity ≤ current capacity O(1) O(1) Just compares values, no reallocation or copying needed.
Average Case Called occasionally during insertions (amortized behavior) O(1) (amortized) O(n) Most of the time, no resize, if resize occurs, its cost spreads over many adds.
Worst Case Requested capacity > current capacity and reallocation happens O(n) O(n) Internal bucket & entry arrays are recreated and existing elements rehashed.

Methods

  1. Add :- public bool Add (T item);
  2. Contains :- public bool Contains (T item);
  3. Clear :- public void Clear ();
  4. CopyTo(T[]) :- public void CopyTo (T[] array);
  5. CopyTo (T[] , int) :- public void CopyTo (T[] array, int arrayIndex);
  6. CopyTo (T[] , int , int) :- public void CopyTo (T[] array, int arrayIndex, int count);
  7. CreateSetComparer() :- public static System.Collections.Generic.IEqualityComparer<System.Collections.Generic.HashSet<T>> CreateSetComparer ();
  8. EnsureCapacity (int ) :- public int EnsureCapacity (int capacity);
  9. Equals (Object) :- public virtual bool Equals (object? obj);
  10. ExceptWith(IEnumerable<T>) :- public void ExceptWith (System.Collections.Generic.IEnumerable<T> other);
  11. GetEnumerator() :- public System.Collections.Generic.HashSet<T>.Enumerator GetEnumerator ();
  12. GetHashCode() :- public virtual int GetHashCode ();
  13. GetType() :- public Type GetType ();
  14. IntersectWith(IEnumerable<T>) :- public void IntersectWith (System.Collections.Generic.IEnumerable<T> other);
  15. IsProperSubsetOf(IEnumerable<T>) :- public bool IsProperSubsetOf (System.Collections.Generic.IEnumerable<T> other);
  16. IsProperSupersetOf(IEnumerable<T>) :- public bool IsProperSupersetOf (System.Collections.Generic.IEnumerable<T> other);
  17. IsSubsetOf(IEnumerable<T>) :- public bool IsSubsetOf (System.Collections.Generic.IEnumerable<T> other);
  18. IsSupersetOf(IEnumerable<T>) :- public bool IsSupersetOf (System.Collections.Generic.IEnumerable<T> other);
  19. MemberwiseClone() :- protected object MemberwiseClone ();

    Creates a shallow copy of the current Object.

  20. OnDeserialization(Object) :-public virtual void OnDeserialization (object? sender);
  21. Overlaps(IEnumerable<T>) :- public bool Overlaps (System.Collections.Generic.IEnumerable<T> other);
  22. Remove(T) :- public bool Remove (T item);
  23. RemoveWhere(Predicate<T>) :- public int RemoveWhere (Predicate<T> match);
  24. SetEquals(IEnumerable<T>) :- public bool SetEquals (System.Collections.Generic.IEnumerable<T> other);
  25. SymmetricExceptWith(IEnumerable<T>) :- public void SymmetricExceptWith (System.Collections.Generic.IEnumerable<T> other);
  26. ToString() :- public virtual string? ToString ();
  27. TrimExcess() :- public void TrimExcess ();
  28. TryGetValue(T, T)public bool TryGetValue (T equalValue, out T actualValue);
  29. UnionWith(IEnumerable<T>) :- public void UnionWith (System.Collections.Generic.IEnumerable<T> other);