HashMap vs Hashtable in Java: Wer gewinnt? Wir schauen unter die Haube
\n\nWenn Sie sich jemals gefragt haben: „Was ist eigentlich der Unterschied zwischen HashMap und Hashtable?“ – sind Sie nicht allein. Dies ist eine der beliebtesten Fragen auf Stack Overflow, und das aus gutem Grund. Beide Klassen speichern Daten im Format „Schlüssel – Wert“, beide verwenden Hashing, aber sie leben in unterschiedlichen Welten. Der eine ist flink und modern, der andere alt, zuverlässig, aber etwas schwerfällig. Werfen wir einen Blick „unter die Haube“ und finden wir heraus, was Sache ist.
\n\nIn diesem Artikel werden wir nicht einfach die Unterschiede auflisten. Wir werden verstehen, wie die interne Mechanik funktioniert, warum eine Klasse null erlaubt und die andere nicht, und wann man ConcurrentHashMap anstelle von beiden verwenden sollte.
Ein kurzer historischer Exkurs
\n\nHashtable erschien in Java bereits in Version 1.0. Es war eine der ersten Sammlungsklassen und von Anfang an synchronisiert. Zu jener Zeit war Multithreading selten, aber die Java-Entwickler beschlossen, auf Nummer sicher zu gehen. Später, in Java 2 (1.2), kamen die neuen Sammlungen aus dem Collections Framework, darunter HashMap. Es war nicht synchronisiert, aber dafür viel schneller. Seitdem gilt Hashtable als veraltet (legacy), obwohl es niemand aus der Sprache entfernt hat.
\n\nDer Hauptunterschied: Synchronisation und Threads
\n\nDer erste und wichtigste Unterschied ist die Synchronisation. Die Methoden von Hashtable (z. B. put(), get(), remove()) sind mit dem Schlüsselwort synchronized versehen. Das bedeutet, dass zwei Threads nicht gleichzeitig dieselbe Methode auf demselben Hashtable-Objekt ausführen können. Das klingt nach einem Vorteil, wird aber in der Praxis zu einem Engpass.
// Hashtable – alle Methoden sind synchronisiert\nHashtable<String, String> table = new Hashtable<>();\ntable.put("key1", "value1"); // threadsicher, aber langsam bei hoher Konkurrenz\n\n// HashMap – nicht synchronisiert\nHashMap<String, String> map = new HashMap<>();\nmap.put("key1", "value1"); // schnell, aber nicht threadsicher\n\n\nWenn Sie in einer Singlethread-Anwendung arbeiten (was die Mehrheit ist), wird HashMap schneller sein, da es keine Zeit für das Erfassen und Freigeben von Sperren aufwenden muss. Wenn Sie jedoch Threadsicherheit benötigen, ist es besser, nicht Hashtable zu verwenden, sondern ConcurrentHashMap (erschienen in Java 5). Diese verwendet eine feinere Sperrung (Segmente oder Sperren auf Bucket-Ebene) und arbeitet in einer Multithread-Umgebung deutlich schneller.
Null, null und nochmals null
\n\nDer zweite wichtige Unterschied ist der Umgang mit null. HashMap erlaubt null sowohl als Schlüssel (genau einmal) als auch als Werte (beliebig oft). Hashtable hingegen wirft eine NullPointerException, wenn Sie versuchen, null zu übergeben.
HashMap<String, String> map = new HashMap<>();\nmap.put(null, "nullKey"); // OK\nmap.put("key", null); // OK\nSystem.out.println(map.get(null)); // "nullKey"\n\nHashtable<String, String> table = new Hashtable<>();\n// table.put(null, "value"); // NullPointerException!\n// table.put("key", null); // NullPointerException!\n\n\nWarum ist das so? Es liegt daran, wie das Hashing funktioniert. In HashMap wird für den Schlüssel null ein spezieller Hash-Code 0 verwendet, und er wird in den nullten Bucket gelegt. Hashtable hingegen verwendet einen alten Mechanismus, bei dem der Aufruf von hashCode() auf null zu einem Fehler führt. Außerdem ist Hashtable nicht auf Null-Werte ausgelegt, da dies die Logik der synchronisierten Operationen verletzen würde.
Interne Funktionsweise: Wie funktioniert eine Hash-Tabelle unter der Haube
\n\nUm den Unterschied tiefer zu verstehen, werfen wir einen Blick ins Innere. Sowohl HashMap als auch Hashtable basieren auf einer Hash-Tabelle. Dies ist ein Array von „Buckets“, wobei jeder Bucket eine verkettete Liste (oder ab Java 8 ein Baum) ist. Wenn Sie ein