HashMap vs Hashtable in Java: Who Wins? Let's Look Under the Hood
\n\nIf you've ever wondered, "What exactly is the difference between HashMap and Hashtable?" — you're not alone. This is one of the most popular questions on Stack Overflow, and for good reason. Both classes store data in key-value format, both use hashing, but they live in different worlds. One is fast and modern, the other is old, reliable, but a bit clunky. Let's look under the hood and figure out what's what.
\n\nIn this article, we won't just list the differences. We'll understand how the internal mechanics work, why one class allows null and the other doesn't, and when to use ConcurrentHashMap instead of both.
A Brief History Lesson
\n\nHashtable appeared in Java back in version 1.0. It was one of the first collection classes, and it was synchronized from the start. In those days, multithreading was rare, but Java developers decided to play it safe. Later, in Java 2 (1.2), new collections from the Collections Framework were released, including HashMap. It was unsynchronized but much faster. Since then, Hashtable has been considered legacy, although it hasn't been removed from the language.
\n\nThe Main Difference: Synchronization and Threads
\n\nThe first and most important difference is synchronization. Hashtable methods (e.g., put(), get(), remove()) are marked with the synchronized keyword. This means two threads cannot execute the same method on the same Hashtable object simultaneously. That sounds like an advantage, but in practice, it becomes a bottleneck.
// Hashtable — all methods are synchronized\nHashtable<String, String> table = new Hashtable<>();\ntable.put("key1", "value1"); // thread-safe, but slow under high contention\n\n// HashMap — not synchronized\nHashMap<String, String> map = new HashMap<>();\nmap.put("key1", "value1"); // fast, but not thread-safe\n\n\nIf you're working in a single-threaded application (which most are), HashMap will be faster because it doesn't waste time acquiring and releasing locks. If you need thread safety, it's better not to use Hashtable, but instead use ConcurrentHashMap (introduced in Java 5). It uses finer-grained locking (segments or bucket-level locks) and performs significantly better in a multithreaded environment.
Null, Null, and More Null
\n\nThe second important difference is handling null. HashMap allows null both as a key (exactly once) and as values (any number of times). Hashtable, on the other hand, will throw a NullPointerException if you try to pass null.
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\nWhy is that? It all comes down to how hashing works. In HashMap, a special hash code of 0 is used for the null key, and it is placed in the zero bucket. Hashtable, however, uses an older mechanism where calling hashCode() on null causes an error. Additionally, Hashtable does not account for null values, as that would break the logic of synchronized operations.
Internal Structure: How a Hash Table Works Under the Hood
\n\nTo understand the difference more deeply, let's look inside. Both HashMap and Hashtable are based on a hash table. This is an array of "buckets," where each bucket is a linked list (or a tree, starting from Java 8). When you add a