HashSet in C# with Examples
HashSet offers an effective method for handling collections of singular components. It belongs to the System.Collections.Generic namespace has several advantages over other collection types, particularly in situations when the uniqueness of the elements is essential.
Creating a HashSet
A HashSet can be easily created. You can begin by declaring a variable of type HashSetT>, initialising it using the constructor, and executing the code. The type of elements that the HashSet will hold are specified by the type of argument T. For instance, you would use HashSetint> to build a HashSet of integers.
Here is an example of initialising a HashSet of strings with some elements:
HashSet<string> mySet = new HashSet<string>()
{
"apple",
"banana",
"mango"
};
In the code above, we declare a string storing HashSet called mySet. The initial members of the set are provided inside the curly braces after using the new keyword to create a new instance of the hash set.
Using HashSet, you may guarantee that each element in the collection is distinct. Duplicate elements will be immediately disregarded if you try to add them, preserving the set's unique feature.
Because a hash table is the fundamental data structure of HashSet, quick element insertion and retrieval are possible. This data structure facilitates quick access to elements by using a hash function to associate elements with buckets.
HashSet offers overloaded constructors and methods for customising the set's behaviour and starting capacity in addition to the default constructor. You can also define a custom equality comparison if you want to compare components according to a certain standard.
Utilising HashSet makes keeping unique collections easier and is helpful for various tasks, including removing duplicate items from a list, verifying that an item exists, and carrying out set operations like union, intersection, and Difference. It is the default option when working with collections in C# due to its effective performance and inherent uniqueness guarantee.
Iterating over a HashSet
In C#, you have many alternatives when iterating through a hash set's components. Utilising a foreach loop and utilising an Enumerator are the two methods used most frequently.
1. Using the Foreach loop
A hash set's components can be iterated overusing a foreach loop. This straightforward approach offers an easy way to reach each set member.
HashSet<string> fruits = new HashSet<string> { "apple", "banana", "orange" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
2. Using Enumerator
The GetEnumerator function of the HashSet class, which implements the IEnumerable interface, enables you to get an Enumerator. The items of the HashSet can then be iterated by using this Enumerator.
HashSet<int> numbers = new HashSet<int> { 1, 2, 3 };
var enumerator = numbers.GetEnumerator();
while (enumerator.MoveNext())
{
int number = enumerator.Current;
Console.WriteLine(number);
}
Adding and Removing elements
To add elements to a HashSet, you can use the Add method. Here is an example:
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
You can remove elements using the remove method.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
numbers.Remove(20);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples1.png)
Modifying Elements
When elements are added to a HashSet, they are regarded as immutable. This means that a HashSet's elements cannot be changed directly. You must remove the element before adding it again in the updated form to achieve the desired result.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<string> names = new HashSet<string>();
// Add some names to the set
names.Add("Vardhan");
names.Add("Vikas");
names.Add("Vathnam");
// Display the initial set
Console.WriteLine("Initial Set:");
foreach (string name in names)
{
Console.WriteLine(name);
}
// Modify an element by removing and re-adding it
if (names.Contains("Vathnam"))
{
names.Remove("Vathnam");
names.Add("Rathnam");
}
// Display the modified set
Console.WriteLine("\nModified Set:");
foreach (string name in names)
{
Console.WriteLine(name);
}
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples2.png)
In this example, we build a HashSet with a string value of names and add some names. To change an element, we first determine whether the desired element is present in the set (Vathnam). If it does, we use the Remove() method to remove the element and the Add() function to add the updated version (Rathnam).
Note: Changing an element in a HashSetT> requires eliminating and re-adding elements, which may affect the set's effectiveness and performance. Consider adopting a different data structure that allows for direct modification, such as a List<T> or a Dictionary<TKey, TValue>, if you regularly need to modify records.
Checking if the elements Exist or not.
HashSet provides several methods to check for the presence of an element.
1. Using Contains method
One way to check if an element exists in a HashSet is by using the Contains method. It will return true if the element is found and false if it is not.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
bool contains = numbers.Contains(30);
Console.WriteLine(contains);
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples3.png)
2. Using Overlaps method: The Overlaps method checks if any elements in one HashSet exist in another HashSet. It returns true if there is at least one common element; otherwise, it returns false.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
HashSet<int> otherNumbers = new HashSet<int> { 30, 40, 50 };
bool hasCommonElements = numbers.Overlaps(otherNumbers);
Console.WriteLine(hasCommonElements);
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples4.png)
3. Using the IntersectsWith method
Determine whether two HashSets share any components by using the IntersectsWith function. If there are shared elements, it returns true; otherwise, it returns false.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
HashSet<int> otherNumbers = new HashSet<int> { 30, 40, 50 };
bool hasCommonElements = numbers.Overlaps(otherNumbers);
Console.WriteLine(hasCommonElements);
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples5.png)
Sorting and Ordering in HashSet
A HashSet<T> does not automatically retain a particular order or offer direct sorting capability. HashSet<T>'s elements are kept unordered for quick access and uniqueness
The HashSetT> can be transformed into another collection type, such as a List<T>, and then sorting operations can be used on that collection to sort or order the elements if necessary. Here's an example:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
HashSet<int> numbers = new HashSet<int>();
// Add some numbers to the set
numbers.Add(5);
numbers.Add(2);
numbers.Add(8);
numbers.Add(3);
numbers.Add(1);
// Convert the set to a list and sort it
List<int> sortedList = numbers.ToList();
sorted list.Sort();
// Display the sorted elements
Console.WriteLine("Sorted Elements:");
foreach (int number in sortedList)
{
Console.WriteLine(number);
}
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples6.png)
Here, we have a HashSet<int> with the name numbers that contain some integer values. We use the ToList() function to change the HashSet<int> into a List<int> and then sort the elements. The elements in the list are then sorted in ascending order using the Sort() method. To display the elements in the proper order, we finally iterate over the sorted list.
The required sorting and ordering capability can be obtained by changing the HashSet<int> to another collection type and performing sorting operations on that collection. Remember that the sorted collection you produce contains the order rather than the HashSet<int> itself.
Searching in HashSet
You can use various techniques to find elements within the set. Here are three popular ways to search a HashSet:
The Contains() method determines whether a particular element is present in the HashSet. If the element is present or not, a boolean value is returned. The Contains() method internally employs an equality comparison and hash code to detect whether an element is present. It has an average temporal complexity of O(1).
The IntersectWith() method lets you identify the items two data collections share. This method will change the original set only to contain the elements shared by the two sets if you pass it another HashSet as an argument. This approach is helpful when you need to locate the intersection of two sets quickly.
The SetEquals() method determines whether two sets are equal, that is, whether they contain the same elements. It accepts a second HashSet as an input and provides a boolean response indicating whether or not the two sets are equal. The SetEquals() method uses an equality comparison and the hash code to compare the elements in the two sets. O(n), where n is the number of elements in the set, is the temporal complexity of the problem.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Create two sets
HashSet<int> set1 = new HashSet<int>() { 1, 2, 3, 4, 5 };
HashSet<int> set2 = new HashSet<int>() { 4, 5, 6, 7, 8 };
// Check if an element exists in the set using Contains()
int elementToCheck = 3;
bool containsElement = set1.Contains(elementToCheck);
Console.WriteLine($"Set1 contains {elementToCheck}: {containsElement}");
// Find the common elements between two sets using IntersectWith()
set1.IntersectWith(set2);
Console.WriteLine("Common elements between Set1 and Set2:");
foreach (int element in set1)
{
Console.WriteLine(element);
}
// Check if two sets are equal using SetEquals()
bool areSetsEqual = set1.SetEquals(set2);
Console.WriteLine($"Set1 and Set2 are equal: {areSetsEqual}");
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples7.png)
Set Operations
Union: A set that contains all of the distinctive items from both sets is the union of two sets, denoted by A and B. In other words, it merges all components from both sets into a single, unique set.
Intersection: A set that contains the elements shared by two sets, denoted by A and B, is called an intersection set. It only contains the components that are included in both sets.
Difference: The set that contains the elements from set A that are absent from set B is the Difference between the two sets, A and B. In other words, it doesn't include the components that both sets share.
Subset: If every element in set A is also present in set B, then set A is said to be a subset of set B. In other words, set B contains all the elements in set A.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Creating two HashSets
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4, 5 };
HashSet<int> set2 = new HashSet<int> { 4, 5, 6, 7, 8 };
// Union operation
HashSet<int> unionSet = new HashSet<int>(set1);
unionSet.UnionWith(set2);
Console.WriteLine("Union:");
foreach (int element in unionSet)
{
Console.WriteLine(element);
}
// Intersection operation
HashSet<int> intersectionSet = new HashSet<int>(set1);
intersectionSet.IntersectWith(set2);
Console.WriteLine("\nIntersection:");
foreach (int element in intersectionSet)
{
Console.WriteLine(element);
}
//Difference operation
HashSet<int> differenceSet = new HashSet<int>(set1);
differenceSet.ExceptWith(set2);
Console.WriteLine("\nDifference:");
foreach (int element in differenceSet)
{
Console.WriteLine(element);
}
// Subset operation
bool isSubset = set1.IsSubsetOf(set2);
Console.WriteLine("\nIs set1 a subset of set2? " + isSubset);
}
}
Output:
![HashSet in C# with Examples](https://static.tutorialandexample.com/csharp/hash-set-in-csharp-with-examples8.png)
The C# HashSet<T> class methods and operators can be used to carry out these set operations. For instance, you can conduct a union operation using the UnionWith() method, an intersection operation using the IntersectWith() method, and a difference operation using the ExceptWith() method.
Note: It is important to remember that when using HashSetT> to conduct set operations, the resulting set will automatically eliminate duplicate elements because HashSet<T> ensures each element is unique.
Performance and Complexity
HashSet's performance and complexity must be considered when working with this data structure. Let's talk about HashSet's performance features and complexity analysis:
Insertion, Deletion, and Searching:
Insertion (Add()): The temporal complexity of adding an element to a hash set is typically O(1). In the worst-case situation, the time complexity, where n is the set's element count, could be O(n). This happens when objects collide, growing the internal bucket structure.
Deletion (Remove()) It takes O(1) on average time to remove an element from a hash set. Similar to insertion, collisions can cause the worst-case time complexity to be O(n).
Searching (Contains()): A HashSet's average time complexity for determining whether a given element is present is O(1). It is important to remember that the Contains() method uses the element's hash code to find the appropriate bucket, producing a constant-time search quickly.
Memory Usage:
A HashSet uses memory in direct proportion to its number of entries. The internal bucket structure expands dynamically as elements are added to the set, effectively accommodating the elements. However, this may result in greater memory utilisation compared to a straightforward array or list.
Performance and Collisions:
When two or more elements generate the same hash code and are stored in the same bucket, this is referred to as a collision. In certain circumstances, HashSet's speed may be affected since it must search through the elements in the bucket to locate a particular element. However, HashSet uses a robust hashing algorithm and bucket structure to reduce collisions and, in most circumstances, provide reliable performance.
Benefits of HashSet
Efficient uniqueness checking: HashSet ensures that all elements are unique, which can be beneficial when dealing with large datasets.
Fast lookup: HashSet provides fast lookup operations due to its hash-based implementation.
Drawbacks of HashSet
Memory usage: HashSet requires additional memory to store hash codes and manage collisions, which can impact memory usage compared to more straightforward collection types like lists or arrays.
Ordering: HashSet does not maintain the order of elements as it focuses on uniqueness and performance.