☕ Java
Hashtable
Hashtable is a legacy hash table implementation that predates the Collections Framework. Like Vector for lists, Hashtable was Java's original hash map from Java 1.0. It is thread-safe because all its methods are synchronised, but it does not permit null keys or null values. Hashtable has been retrofitted to implement the Map interface but should never be used in new code — HashMap is the non-concurrent replacement and ConcurrentHashMap is the concurrent replacement.
Hashtable — Design, Limitations, and Why to Avoid It
Hashtable was introduced in Java 1.0 as the standard hash map before any Collections Framework existed. Like Vector, every public method is declared synchronized, making it thread-safe at the cost of performance. All threads share a single lock — the Hashtable object itself — so concurrent operations on the map are completely serialised.
The two most significant differences from HashMap are null handling and synchronisation. Hashtable does not permit null keys or null values. Attempting to put a null key or null value throws NullPointerException immediately. HashMap permits one null key and any number of null values. This stricter null policy is a design decision from Java 1.0 that prioritised explicit failure over silent null propagation.
Hashtable's synchronisation model has the same problems as Vector's: method-level synchronisation prevents concurrent access but does not make compound operations atomic, and it serialises all operations including reads that could safely proceed concurrently. The synchronisation overhead is paid even in single-threaded code.
Hashtable also retains legacy methods that duplicate the Map interface: keys() returns an Enumeration (the pre-Iterator way to iterate), elements() returns an Enumeration of values, and contains() is equivalent to containsValue(). These legacy methods exist solely for backward compatibility.
The Javadoc for Hashtable states explicitly: "If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable." This is the definitive guidance — there is no use case for Hashtable in new Java code.
Java
// ── Hashtable — thread-safe but legacy: ──────────────────────────────
Hashtable<String, Integer> ht = new Hashtable<>();
ht.put("Alice", 90);
ht.put("Bob", 85);
// ── No null keys or values: ───────────────────────────────────────────
try {
ht.put(null, 42); // NullPointerException!
} catch (NullPointerException e) {
System.err.println("Hashtable does not allow null keys");
}
try {
ht.put("Carol", null); // NullPointerException!
} catch (NullPointerException e) {
System.err.println("Hashtable does not allow null values");
}
// HashMap DOES allow both: map.put(null, 42) and map.put("key", null)
// ── Legacy Enumeration API: ───────────────────────────────────────────
Enumeration<String> keys = ht.keys(); // pre-Iterator key iteration
Enumeration<Integer> values = ht.elements(); // pre-Iterator value iteration
while (keys.hasMoreElements()) {
System.out.println(keys.nextElement());
}
// ── contains() is containsValue(): ───────────────────────────────────
System.out.println(ht.contains(90)); // true — contains VALUE 90
System.out.println(ht.containsValue(90)); // true — same thing
System.out.println(ht.containsKey("Alice")); // true
// ── Modern Map API also works (retrofitted): ─────────────────────────
for (Map.Entry<String, Integer> e : ht.entrySet()) {
System.out.printf("%s → %d%n", e.getKey(), e.getValue());
}
// ── Migration guide: ──────────────────────────────────────────────────
// Hashtable ht = new Hashtable<>();
//
// Single-threaded replacement:
Map<String, Integer> modern = new HashMap<>();
//
// Concurrent replacement:
Map<String, Integer> concurrent = new ConcurrentHashMap<>();
//
// Note: ConcurrentHashMap also forbids null keys and values (like Hashtable)
// HashMap permits null key/values (unlike both)Related Topics in Collections Framework
HashMap
HashMap is the most widely used Map implementation in Java. It stores key-value pairs in a hash table, providing average O(1) performance for get, put, remove, and containsKey operations. Keys are stored in no guaranteed order. HashMap permits one null key and multiple null values. It is not thread-safe. Understanding how HashMap uses hash codes, handles collisions, and resizes is foundational knowledge for writing efficient Java code.
LinkedHashMap
LinkedHashMap extends HashMap and maintains a doubly-linked list running through all its entries, preserving the order in which entries were inserted. Iteration over a LinkedHashMap always returns entries in insertion order. Optionally, it can be constructed in access-order mode where entries are ordered by most-recently accessed, making it the perfect foundation for implementing a Least Recently Used (LRU) cache. LinkedHashMap has slightly higher memory overhead than HashMap due to the extra linked list pointers.
TreeMap
TreeMap is a Red-Black tree implementation of the NavigableMap interface. It stores key-value pairs in sorted order — either the natural ordering of keys (requiring them to implement Comparable) or a custom Comparator provided at construction. All basic operations (get, put, remove, containsKey) are O(log n). TreeMap provides rich navigation operations: finding the closest key, extracting sub-maps, and headMap/tailMap views.
ConcurrentHashMap
ConcurrentHashMap is a thread-safe, high-performance hash map optimised for concurrent access. Unlike Hashtable and synchronised HashMap (which use a single lock), ConcurrentHashMap uses a more sophisticated concurrency mechanism — in Java 8+, it uses CAS (compare-and-swap) operations and per-bucket synchronisation — allowing multiple threads to read and write to different parts of the map simultaneously without blocking each other. It does not permit null keys or null values.