How To C# HashSet (Tutorial): From A to Z

May 6, 2023 | .NET, C#

C# HashSet might just be the secret ingredient you’ve been missing in your quest to optimize your code. In this article, we’ll take a deep dive into the various aspects of the HashSet type, from understanding the basics to exploring advanced usage scenarios. So, are you ready to level up your C# skills? Let’s get started!

A Deep Dive into C# HashSet: Unearthing its Features and Usage

Before diving into the code, let’s take a brief moment to appreciate the versatility and efficiency offered by C# HashSet. In the upcoming sections, we’ll explore the fundamentals of HashSet, understand its unique characteristics, and even compare it with other collection types to help you make an informed choice.

What is a HashSet in C# and Why Should You Use It?

As a C# developer, mastering collections is crucial to efficiently handle data in your applications. One such powerful collection is C# HashSet. Here, we’ll take a closer look at what makes HashSet unique and when it can benefit your code:

  • The basics: A HashSet is a collection that holds unique elements in no particular order (O(1) complexity for adding, searching or removing). The HashSet<T> is a generic class in the System.Collections.Generic namespace, ideal for managing large data sets and performing set operations.
  • Core aspects: The C# HashSet is a hash-based collection that allows only distinct elements. It supports various operations such as Union, Intersection, Difference, and more.

Why should you use a C# HashSet? Well, primarily because of its efficient performance and built-in set operations. But we’ll uncover more advantages as we journey through this article.

C# HashSet vs Dictionary, List, and Set

Choose wisely, Developer! The right collection type can make or break your code. To help you make an informed decision, let’s evaluate the differences between HashSet, Dictionary, List, and Set with a more comprehensive approach, in-depth explanations, and code examples.

Hashset vs Set

Here’s the key difference— HashSet is implemented using a hash table, resulting in faster retrieval and insertion times compared to other collection types like SortedSet (which uses a tree-based implementation). Still wondering when to choose which? Consider the following:

  • Use HashSet when the order doesn’t matter and you need quicker set operation results, with no required duplicate elements.
  • Use SortedSet when you need a sorted list of unique items but with slower operations due to the tree structure.

Let’s take a look at creating and using a HashSet and SortedSet in code to understand the differences better:

// Creating a HashSet
HashSet<int> myHashSet = new HashSet<int> {1, 2, 3, 4, 5};

// Creating a SortedSet
SortedSet<int> mySortedSet = new SortedSet<int> {1, 2, 3, 4, 5};

// Adding elements
myHashSet.Add(6);
mySortedSet.Add(6);

// Retrieving elements
foreach (int item in myHashSet)
{
    Console.WriteLine(item); // Output won't have any specific order
}

foreach (int item in mySortedSet)
{
    Console.WriteLine(item); // Output will be in ascending order
}

C# HashSet vs Dictionary

Is it a bird? Is it a plane? No, it’s a KeyValuePair! While the HashSet stores only unique elements, Dictionary stores key-value pairs, with unique keys. Use Dictionary when you require a “lookup table” type of structure, and HashSet when you only need a unique collection without any associated values. Let’s understand this with an example:

// Creating a HashSet
HashSet<string> uniqueNamesHashSet = new HashSet<string> {"Alice", "Bob", "Carol"};

// Creating a Dictionary
Dictionary<string, int> ageDictionary = new Dictionary<string, int>
{
    {"Alice", 25},
    {"Bob", 30},
    {"Carol", 28},
};

// Adding values to HashSet
uniqueNamesHashSet.Add("David");

// Adding values to Dictionary
ageDictionary.Add("David", 34);

// Retrieving values from HashSet
foreach (string name in uniqueNamesHashSet)
{
    Console.WriteLine(name);
}

// Retrieving values from Dictionary
foreach (KeyValuePair<string, int> element in ageDictionary)
{
    Console.WriteLine($"{element.Key}: {element.Value}");
}

C# HashSet vs List

List stores elements in sequential order, allowing duplicate elements and supports indexed access. HashSet, on the other hand, emphasizes uniqueness and faster performance. Choose List when the order of elements plays a critical role, and HashSet when you need to optimize for set operations and speed. To illustrate the differences, let’s compare HashSet and List in code:

// Creating a List
List<int> myList = new List<int> {1, 2, 3, 4, 5};

// Creating a HashSet
HashSet<int> myHashSet = new HashSet<int> {1, 2, 3, 4, 5};

// Adding elements to List
myList.Add(2); // List allows duplicates

// Adding elements to HashSet
bool result = myHashSet.Add(2); // HashSet doesn't allow duplicates, result will be False

// Indexed access in List
int firstElement = myList[0]; // firstElement will hold the value 1

// Indexed access is not allowed in HashSet
// int firstElement = myHashSet[0]; // This line will cause a compilation error

Take your pick according to your needs, and optimize your code using the collection type that best suits your use case. Now you’ve grasped a clearer view of C# HashSet and its counterparts, allowing you to make informed decisions as your code evolves.

Mastering the Art of HashSet Manipulation and Methods

With great power comes great responsibility—or, in the case of HashSet, great methods! Now that you’ve chosen to venture into the realm of HashSet, it’s time to uncover the secrets that lie within its myriad methods and properties, and learn how to fully harness their potential.

// Create a new hash set to hold unique integers
HashSet<int> intHashSet = new HashSet<int>();

// Add elements using the Add method
intHashSet.Add(42);
intHashSet.Add(7);
bool added = intHashSet.Add(42); // Returns false, because 42 is already present

Console.WriteLine("Hash set contains 42: " + intHashSet.Contains(42)); // Outputs: "Hash set contains 42: True"

That’s just the beginning! C# HashSet offers a plethora of methods and set operations that’ll make your code more efficient than ever. Here’s a rundown of some useful methods and their real-life applications:

Union, Intersect, and Except: Performing Set Operations with Ease

HashSet<int> firstSet = new HashSet<int> { 1, 2, 3 };
HashSet<int> secondSet = new HashSet<int> { 2, 3, 4 };

// Perform a union
firstSet.UnionWith(secondSet); // firstSet now contains {1, 2, 3, 4}

// Perform an intersection
firstSet.IntersectWith(secondSet); // firstSet now contains {2, 3}

// Perform a difference (except)
firstSet.ExceptWith(secondSet); // firstSet now contains {1}

These set operations allow you to combine, intersect, and subtract the elements of two collections, providing you with a powerful tool to manage your data.

Clear, Remove and Count: Housekeeping Methods for Your HashSet

HashSet<string> myHashSet = new HashSet<string> {"apple", "orange", "banana"};

// Remove an element
myHashSet.Remove("apple"); // myHashSet now contains {"orange", "banana"}

// Clear all elements
myHashSet.Clear(); // myHashSet is empty

// Count the number of elements
int count = myHashSet.Count; // count will be 2 before Clear() and 0 after Clear()

Keep your code clean with these housekeeping methods, and make sure you can always count on your HashSet.

Advanced HashSet Usage: Going Beyond the Basics

Apart from the standard methods, here are some advanced HashSet practices that might come in handy, complete with code examples:

Implement Custom Comparer Objects for HashSet

Control how HashSet elements are compared for equality by extending the EqualityComparer<T> class and implementing custom comparer objects.

class CaseInsensitiveComparer : EqualityComparer<string>
{
    public override bool Equals(string x, string y)
    {
        return x.Equals(y, StringComparison.OrdinalIgnoreCase);
    }

    public override int GetHashCode(string obj)
    {
        return obj.ToUpperInvariant().GetHashCode();
    }
}

HashSet<string> caseInsensitiveHashSet = new HashSet<string>(new CaseInsensitiveComparer())
{
    "apple", "banana", "orange"
};

bool added = caseInsensitiveHashSet.Add("APPLE"); //Returns false, because "apple" is considered equal when using custom comparer.

Efficient HashSet Iteration Using Enumerator

Use HashSet<T>.Enumerator to iterate through elements more efficiently when using “for-each” loops.

HashSet<int> myHashSet = new HashSet<int> { 1, 2, 3, 4, 5 };

// Using for-each loop with implicit enumerator
foreach (int item in myHashSet)
{
    Console.WriteLine(item);
}

// Using explicit HashSet enumerator for more efficient iteration
HashSet<int>.Enumerator enumerator = myHashSet.GetEnumerator();
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current);
}

With this advanced knowledge and code examples, you’re equipped to unlock the full potential of HashSet in your C# code. Embrace these methods and practices to truly harness the power of C# HashSet and tackle even the most challenging scenarios with confidence. Happy coding!

You May Also Like

Sign up For Our Newsletter

Weekly .NET Capsules: Short reads for busy devs.

  • NLatest .NET tips and tricks
  • NQuick 5-minute reads
  • NPractical code snippets
.