Hashtable
Hashtable is one of Java’s original collection classes — it has been around since Java 1.0. It stores key-value pairs using a hash table internally and is fully synchronized, making every method thread-safe out of the box.
What is Hashtable?
Hashtable<K, V> lives in java.util and implements the Map interface (since Java 2, when the Collections Framework was introduced). Like HashMap, it maps unique keys to values using hashing — but with two important differences: all methods are synchronized, and neither keys nor values can be null.
import java.util.Hashtable;
public class BasicHashtable {
public static void main(String[] args) {
Hashtable<String, Integer> scores = new Hashtable<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Carol", 92);
System.out.println(scores.get("Bob"));
System.out.println(scores);
}
}
Output:
87
{Carol=92, Alice=95, Bob=87}
Note:
Hashtabledoes not guarantee any particular iteration order. The output order you see depends on the hash codes of the keys.
Creating a Hashtable
import java.util.Hashtable;
public class CreateHashtable {
public static void main(String[] args) {
// 1. Default constructor (initial capacity 11, load factor 0.75)
Hashtable<String, Integer> ht1 = new Hashtable<>();
// 2. Specify initial capacity
Hashtable<String, Integer> ht2 = new Hashtable<>(20);
// 3. Specify both initial capacity and load factor
Hashtable<String, Integer> ht3 = new Hashtable<>(20, 0.5f);
}
}
The initial capacity controls how many buckets are created upfront. The load factor (default 0.75) determines when the table rehashes — at 75% fullness by default.
Tip: The default initial capacity for
Hashtableis 11 (prime), whileHashMapuses 16 (power of two). This is a deliberate historical difference.
Common Methods
| Method | Description |
|---|---|
put(K key, V value) | Inserts or replaces a key-value pair |
get(Object key) | Returns the value for a key, or null if not found |
remove(Object key) | Removes and returns the value for a key |
containsKey(Object key) | Returns true if the key exists |
containsValue(Object value) | Returns true if the value exists |
size() | Number of key-value pairs |
isEmpty() | Returns true if the table is empty |
keySet() | Returns a Set of all keys |
values() | Returns a Collection of all values |
entrySet() | Returns a Set of all Map.Entry pairs |
clear() | Removes all mappings |
keys() | Returns an Enumeration of keys (legacy) |
elements() | Returns an Enumeration of values (legacy) |
import java.util.Hashtable;
import java.util.Enumeration;
public class HashtableMethods {
public static void main(String[] args) {
Hashtable<String, String> capitals = new Hashtable<>();
capitals.put("India", "New Delhi");
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");
// Check and retrieve
System.out.println(capitals.containsKey("France")); // true
System.out.println(capitals.containsValue("Berlin")); // false
System.out.println(capitals.size()); // 3
// Legacy Enumeration iteration
Enumeration<String> keys = capitals.keys();
while (keys.hasMoreElements()) {
String k = keys.nextElement();
System.out.println(k + " -> " + capitals.get(k));
}
}
}
Output:
true
false
3
Japan -> Tokyo
France -> Paris
India -> New Delhi
Iterating Over a Hashtable
You can iterate using the modern entrySet() approach (preferred) or the legacy Enumeration.
import java.util.Hashtable;
import java.util.Map;
public class IterateHashtable {
public static void main(String[] args) {
Hashtable<String, Integer> population = new Hashtable<>();
population.put("China", 1400);
population.put("India", 1380);
population.put("USA", 331);
// Modern: entrySet (preferred)
for (Map.Entry<String, Integer> entry : population.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue() + "M");
}
System.out.println("---");
// Modern: forEach (Java 8+)
population.forEach((country, pop) ->
System.out.println(country + " = " + pop + "M"));
}
}
Output:
USA: 331M
India: 1380M
China: 1400M
---
USA = 331M
India = 1380M
China = 1400M
No Null Keys or Values
Attempting to insert a null key or null value throws a NullPointerException immediately.
import java.util.Hashtable;
public class NullDemo {
public static void main(String[] args) {
Hashtable<String, String> ht = new Hashtable<>();
try {
ht.put(null, "value"); // throws NullPointerException
} catch (NullPointerException e) {
System.out.println("Null key not allowed!");
}
try {
ht.put("key", null); // throws NullPointerException
} catch (NullPointerException e) {
System.out.println("Null value not allowed!");
}
}
}
Output:
Null key not allowed!
Null value not allowed!
Warning: This is a common trap when migrating from
HashMaptoHashtable.HashMapaccepts onenullkey and multiplenullvalues;Hashtableaccepts neither.
Thread Safety
Every public method in Hashtable is declared synchronized, so only one thread can execute any method at a time. This makes Hashtable safe to share across threads without any extra locking.
import java.util.Hashtable;
public class ThreadSafeDemo {
public static void main(String[] args) throws InterruptedException {
Hashtable<String, Integer> counter = new Hashtable<>();
counter.put("hits", 0);
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
// get and put are each individually synchronized
counter.put("hits", counter.get("hits") + 1);
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Hits: " + counter.get("hits"));
}
}
Warning: Even though each
getandputis individually atomic, the compound operationget + putabove is not atomic. You can still have race conditions when reading then writing. For true atomic compound operations, preferConcurrentHashMapfromjava.util.concurrent.
Under the Hood
Data Structure
Hashtable uses an array of Entry<K,V> linked lists (buckets). When you call put(key, value):
key.hashCode()is called and then re-hashed internally.- The result is masked with the bucket array length to find the bucket index.
- The entry is prepended (in older Java) or appended to the bucket’s linked list.
- If a key already exists in the bucket (checked via
equals()), the old value is replaced.
Rehashing
When the number of entries exceeds capacity × loadFactor, Hashtable rehashes: a new array roughly twice the current capacity is allocated, and all existing entries are redistributed. This is an O(n) operation, so choosing a good initial capacity matters for performance.
Synchronization Mechanism
Every method acquires the intrinsic lock of the Hashtable instance itself (synchronized(this)). This is coarse-grained — even unrelated read operations block each other. This is why ConcurrentHashMap (which uses fine-grained segment or bucket-level locking / CAS operations since Java 8) massively outperforms Hashtable under concurrent load.
Initial Capacity: 11 vs 16
Hashtable chose a prime number (11) as the default capacity because prime-sized tables distribute keys more evenly when using simple modulo hashing. HashMap chose a power of two (16) because it can replace expensive modulo with a fast bitwise AND — and compensates for distribution with a stronger secondary hash function.
Hashtable vs HashMap — Quick Comparison
| Feature | Hashtable | HashMap |
|---|---|---|
| Thread safety | Yes (synchronized) | No |
| Null keys | Not allowed | One allowed |
| Null values | Not allowed | Allowed |
| Iteration order | Not guaranteed | Not guaranteed |
| Performance | Slower (lock overhead) | Faster |
| Legacy status | Yes (Java 1.0) | No (Java 2) |
| Preferred today? | Rarely | Yes |
Tip: In modern Java, prefer
HashMapfor single-threaded code andConcurrentHashMap(see Concurrent Collections) for multi-threaded code.Hashtableis kept for backward compatibility.
For a more detailed side-by-side analysis, see HashMap vs Hashtable.
When to Use Hashtable
- You are maintaining legacy code that already uses
Hashtableand cannot be refactored. - You need a quick drop-in synchronized map and the performance penalty of coarse locking is acceptable.
In any new code, ConcurrentHashMap is almost always the better thread-safe choice.
Related Topics
- HashMap — the modern, unsynchronized alternative that is preferred in most cases
- HashMap vs Hashtable — detailed feature-by-feature comparison
- LinkedHashMap — HashMap that maintains insertion order
- TreeMap — sorted Map backed by a Red-Black tree
- Concurrent Collections —
ConcurrentHashMapand other thread-safe collections - Map Interface — the common contract all Map types implement