Dictionary vs Hashtable: Der Kampf der Sammlungen in C#
\n\nStellen Sie sich folgende Situation vor: Sie schreiben Code in C# und müssen Daten schnell nach einem Schlüssel speichern und abrufen. Sie öffnen die Dokumentation und sehen zwei ähnliche Klassen — Dictionary und Hashtable. Beide arbeiten nach dem Prinzip "Schlüssel-Wert", beide versprechen blitzschnellen Zugriff auf Daten. Aber welche sollen Sie wählen?
Wenn Sie sich diese Frage schon einmal gestellt haben, sind Sie nicht allein. Auf Stack Overflow hat das Thema "Why is Dictionary preferred over Hashtable in C#?" tausende Aufrufe und hunderte Stimmen gesammelt. Und heute werden wir diese Frage ein für alle Mal klären. Spoiler: Dictionary gewinnt fast immer. Aber lassen Sie uns herausfinden, warum.
\n\nEin kurzer historischer Exkurs: Woher kommt Hashtable?
\n\nUm das Wesen des Konflikts zu verstehen, müssen wir in die Geschichte von C# eintauchen. Als die .NET-Plattform gerade erschien (Version 1.0), stand Entwicklern die Klasse Hashtable aus dem Namensraum System.Collections zur Verfügung. Es war eine klassische Hash-Tabelle, die beliebige Objekte speichern konnte. Klingt cool, oder? Aber es gab ein Problem — das Fehlen einer Typisierung.
Mit der Veröffentlichung von .NET 2.0 und dem Aufkommen von Generics stellte Microsoft eine neue Sammlung vor — Dictionary<TKey, TValue> aus dem Namensraum System.Collections.Generic. Und hier begann die wahre Magie.
Typsicherheit: Der Hauptvorteil von Dictionary
\n\nSchauen wir uns ein konkretes Codebeispiel an. So sieht die Arbeit mit Hashtable aus:
// Alter Weg: Hashtable\nHashtable hashtable = new Hashtable();\nhashtable.Add("Schlüssel1", 123);\nhashtable.Add("Schlüssel2", "Zeichenkette"); // Fehler? Nein, wird kompiliert!\nhashtable.Add(456, "noch eine Zeichenkette"); // Der Schlüssel kann auch ein beliebiges Objekt sein\n\n// Jetzt versuchen wir, Daten abzurufen\nint value = (int)hashtable["Schlüssel2"]; // Autsch! InvalidCastException zur Laufzeit!\n\nSehen Sie das Problem? Hashtable akzeptiert beliebige Typen — sowohl für Schlüssel als auch für Werte. Der Compiler lässt jeden Fehler durch, und Sie erfahren davon erst zur Laufzeit des Programms. Das ist ein direkter Weg zu schwer auffindbaren Bugs.
\n\nUnd jetzt schauen wir uns Dictionary an:
\n\n// Neuer, korrekter Weg: Dictionary\nDictionary<string, int> dictionary = new Dictionary<string, int>();\ndictionary.Add("Schlüssel1", 123);\n// dictionary.Add("Schlüssel2", "Zeichenkette"); // Compilerfehler! Typ stimmt nicht überein\n// dictionary.Add(456, "noch eine Zeichenkette"); // Auch ein Fehler — der Schlüssel muss ein string sein\n\n// Daten ohne Typumwandlung abrufen\nint value = dictionary["Schlüssel1"]; // Sauber, sicher, elegant\n\nDictionary ist typsicher. Sie geben explizit an, welche Typen die Schlüssel und Werte haben. Der Compiler überprüft dies zur Build-Zeit. Keine unerwarteten Ausnahmen zur Laufzeit — der Code funktioniert entweder oder wird nicht kompiliert.
\n\nLeistung: Wer ist schneller?
\n\nDer zweite wichtige Aspekt ist die Leistung. Und auch hier liegt Dictionary vorn. Warum? Es geht um Boxing und Unboxing.
\n\nWenn Sie mit Hashtable arbeiten, werden alle Werttypen (int, double, struct usw.) in Objekte verpackt. Dies belastet den Garbage Collector und verlangsamt die Arbeit. Lassen Sie uns das in der Praxis überprüfen:
\n\n// Hashtable: Boxing/Unboxing\nHashtable ht = new Hashtable();\nStopwatch sw = Stopwatch.StartNew();\nfor (int i = 0; i < 1000000; i++)\n{\n ht.Add(i, i); // Jeder int wird in object verpackt\n}\nsw.Stop();\nConsole.WriteLine($"Hashtable Hinzufügen: {sw.ElapsedMilliseconds} ms");\n\n// Dictionary: ohne Boxing\nDictionary<int, int> dict = new Dictionary<int, int>();\nsw.Restart();\nfor (int i = 0; i < 1000000; i++)\n{\n dict.Add(i, i); // Kein Boxing — wir arbeiten direkt mit int\n}\nsw.Stop();\nConsole.WriteLine($"Dictionary Hinzufügen: {sw.