Wat is een HashSet in C#?

De HashSet<T>-klasse in C# is een verzameling van unieke waarden zonder een specifieke volgorde. Het wordt gebruikt om snel gegevens op te slaan en op te halen zonder duplicaten. Een HashSet kan nuttig zijn wanneer je alleen unieke elementen nodig hebt, zoals een verzameling unieke gebruikersnamen of unieke identificaties.

In tegenstelling tot lijsten of arrays heeft een HashSet geen vaste volgorde, wat betekent dat elementen niet altijd in dezelfde volgorde worden opgeslagen als waarin ze zijn toegevoegd. Hierdoor werkt de HashSet vaak sneller bij het toevoegen, controleren en verwijderen van elementen.

Hoe zit een HashSet in elkaar?

De kern van een HashSet is gebaseerd op hashing, waarbij elk element wordt toegewezen aan een hashcode. Deze hashcode bepaalt waar het element in de onderliggende datastructuur wordt opgeslagen. Op deze manier wordt vergeleken of elementen dezelfde waarden hebben of niet en dus wel of niet moeten worden toegevoegd.

Belangrijk om te weten is dat een HashSet geen indexering ondersteunt zoals een list of array. Dit betekent dat je geen elementen kunt benaderen op basis van een positie, maar alleen kunt itereren over de elementen.

Een HashSet in C# kan als volgt worden geïnitialiseerd:

HashSet<int> uniekeNummers = new HashSet<int>();

In dit voorbeeld wordt een lege HashSet gemaakt die hele getallen (int) opslaat. Je kunt ook elementen toevoegen tijdens de initiële declaratie:

HashSet<string> namen = new HashSet<string> { "John", "Emma", "Sam" };

Hoe gebruik je een Set?

Het belangrijkste voordeel van een HashSet is dat deze snelle prestaties biedt bij bewerkingen zoals het controleren of een element al aanwezig is, het toevoegen van unieke elementen en het verwijderen van items. Als het gaat om het beheren van een verzameling van unieke items zonder noodzaak van een specifieke volgorde, kan een HashSet vaak handiger zijn dan een List.

Tijdcomplexiteit

In termen van tijdcomplexiteit biedt een HashSet doorgaans een O(1) (constante tijd) voor de bewerkingen wat erg efficiënt is voor grotere datasets.

Gebruik een HashSet wanneer:

  • Je alleen unieke elementen wilt opslaan.

  • De volgorde van de elementen niet belangrijk is.

  • Snelle zoek-, toevoeg- en verwijderbewerkingen vereist zijn.

In dit voorbeeld gebruiken we een HashSet om een verzameling unieke gebruikersnamen te beheren:

Casus

Stel je wilt alle gebruikte gebruikersnamen bijhouden, zodat je tijdens registratie snel kan controleren of deze al in gebruik is.

Mogelijke uitwerking

HashSet<string> gebruikersnamen = new HashSet<string>(); gebruikersnamen.Add("JohnDoe"); 
gebruikersnamen.Add("Emma123"); 
gebruikersnamen.Add("JohnDoe"); // Dubbel, dus wordt niet toegevoegd
Console.WriteLine("Aantal unieke gebruikersnamen: " + gebruikersnamen.Count);

Dit resulteert in:

Aantal unieke gebruikersnamen: 2

Beperkingen van HashSet

  • Geen volgorde: Elementen in een HashSet hebben geen specifieke volgorde. Als de volgorde van de gegevens belangrijk is, overweeg dan een andere structuur zoals List of SortedSet.
  • Alleen unieke elementen: Een HashSet staat geen duplicaten toe. Pogingen om een reeds aanwezig element toe te voegen worden genegeerd.
  • Hoge geheugenbelasting bij grote collecties: Hoewel een HashSet snel is, kan het meer geheugen gebruiken dan sommige andere datastructuren.

Bewerkingen voor een HashSet

Add

Add kan gebruikt worden om een element toe te voegen aan een HashSet.

HashSet<string> hashSet = new HashSet<string>();
 
hashSet.Add("Appel");
hashSet.Add("Banaan");
hashSet.Add("Kers");
hashSet.Add("Appel");  // Wordt genegeerd omdat "Appel" al aanwezig is
Console.WriteLine(string.Join(", ", hashSet)); // Output: Appel, Banaan, Kers

Contains

Contains kan gebruikt worden om te checken of een element in de HashSet aanwezig is.

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("Appel");
hashSet.Add("Banaan");
hashSet.Add("Kers");
 
bool bevatBanaan = hashSet.Contains("Banaan");
Console.WriteLine(bevatBanaan); // Output: True
 
bool bevatMango = hashSet.Contains("Mango");
Console.WriteLine("Bevat 'Mango': " + bevatMango); // Output: False

Remove

Remove kan gebruikt worden om een element uit de HashSet te verwijderen.

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("Appel");
hashSet.Add("Banaan");
hashSet.Add("Kers");
 
hashSet.Remove("Banaan");
 
Console.WriteLine(string.Join(", ", hashSet)); // Output: Appel, Kers

Count

Count kan gebruikt worden om het aantal elementen in een HashSet te weten.

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("Appel");
hashSet.Add("Banaan");
hashSet.Add("Kers");
 
hashSet.Remove("Banaan");
 
Console.WriteLine(hashSet.Count); // Output: 2
 

Clear

Clear kan gebruikt worden om een HashSet leeg te maken.

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("Appel");
hashSet.Add("Banaan");
 
hashSet.Clear();
Console.WriteLine(hashSet.Count); // Output: 0

UnionWith

UnionWith voegt de elementen van een andere HashSet toe aan de HashSet zonder duplicaten.

HashSet<int> setA = new HashSet<int> { 1, 2, 3 };
HashSet<int> setB = new HashSet<int> { 3, 4, 5 };
setA.UnionWith(setB); // setA bevat nu { 1, 2, 3, 4, 5 }

IntersectWith

IntersectWith zorgt ervoor dat in een HashSet alleen de elementen behouden worden die overeenkomen tussen de HashSets houdt de elementen over die in de HashSet waar de methode wordt aangeroepen.

HashSet<int> setA = new HashSet<int> { 1, 2, 3 };
HashSet<int> setB = new HashSet<int> { 3, 4, 5 };
setA.IntersectWith(setB); // setA bevat nu alleen {3}

ExceptWith

ExceptWith verwijdert de elementen die in een andere verzameling voorkomen

HashSet<int> setA = new HashSet<int> { 1, 2, 3 };
HashSet<int> setB = new HashSet<int> { 3, 4, 5 };
setA.ExceptWith(setB); // setA bevat nu {1, 2}

Volgende stap: Stappen HashSet