☕ 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
Collections Overview
The Java Collections Framework (JCF) is a unified architecture for representing and manipulating groups of objects. It provides a set of interfaces that define the operations a collection must support, a set of abstract classes that provide partial implementations, and a set of concrete implementations optimised for different use cases. Every Java developer uses collections daily — lists for sequences, sets for uniqueness, maps for key-value pairs, and queues for ordering — and choosing the right implementation for the right use case is one of the most fundamental practical skills in Java.
Iterable
Iterable<E> is the root interface of the Java Collections hierarchy. Any class that implements Iterable can be used in a for-each loop. It declares a single abstract method: iterator(), which returns an Iterator<E> that the for-each loop uses to traverse the elements. Implementing Iterable is all that is required to make a custom data structure work with Java's enhanced for loop, the Stream API, and any method that accepts an Iterable.
Collection Interface
Collection<E> is the root interface of the main collection hierarchy, extending Iterable<E>. It defines the common operations that all collection types must support: adding elements, removing elements, checking containment, querying size, clearing, converting to an array, and bulk operations. List, Set, and Queue all extend Collection. Map does not extend Collection because a map operates on key-value pairs rather than individual elements.
ArrayList
ArrayList is a resizable-array implementation of the List interface. It is the most commonly used collection in Java, providing dynamic sizing on top of a standard array. Elements are stored in contiguous memory, enabling O(1) random access by index. When the internal array is full, ArrayList automatically allocates a larger array and copies all elements. ArrayList is not thread-safe and preserves insertion order, allowing duplicates.