What is the Difference Between HashMap and Hashtable in Java?
Both store key-value pairs, but HashMap is non-synchronized (better performance) while Hashtable is synchronized (thread-safe).
1. HashMap (Non-Synchronized)
import java.util.*;
public class HashMapExample {
public static void main(String[] args) {
Map hashMap = new HashMap<>();
// Allows null key and null values
hashMap.put(null, "NullKey"); // One null key allowed
hashMap.put("Key1", null); // Multiple null values allowed
hashMap.put("Key2", "Value2");
hashMap.put("Key3", "Value3");
System.out.println("HashMap: " + hashMap);
// Not thread-safe - needs external synchronization
Map syncMap = Collections.synchronizedMap(hashMap);
// Iterator is fail-fast
Iterator it = hashMap.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
// hashMap.put("NewKey", "Value"); // ConcurrentModificationException
if (key != null && key.equals("Key2")) {
it.remove(); // Safe removal
}
}
System.out.println("After removal: " + hashMap);
}
}
2. Hashtable (Synchronized - Legacy)
public class HashtableExample {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable<>();
// Does NOT allow null keys or null values
// hashtable.put(null, "Value"); // NullPointerException
// hashtable.put("Key", null); // NullPointerException
hashtable.put("Key1", "Value1");
hashtable.put("Key2", "Value2");
hashtable.put("Key3", "Value3");
System.out.println("Hashtable: " + hashtable);
// Thread-safe (synchronized methods)
// Can be used by multiple threads without external sync
// Enumeration (legacy) instead of Iterator
Enumeration keys = hashtable.keys();
System.out.println("Keys using Enumeration:");
while (keys.hasMoreElements()) {
String key = keys.nextElement();
System.out.println(key + " = " + hashtable.get(key));
}
// Get all values using Enumeration
Enumeration values = hashtable.elements();
System.out.println("Values:");
while (values.hasMoreElements()) {
System.out.println(values.nextElement());
}
}
}
Performance Comparison
public class PerformanceCompare {
public static void main(String[] args) {
int size = 500000;
// HashMap - Faster (no synchronization overhead)
Map hashMap = new HashMap<>();
long start = System.nanoTime();
for (int i = 0; i < size; i++) {
hashMap.put(i, "Value" + i);
}
long hashPutTime = System.nanoTime() - start;
start = System.nanoTime();
for (int i = 0; i < size; i++) {
hashMap.get(i);
}
long hashGetTime = System.nanoTime() - start;
// Hashtable - Slower (synchronized)
Hashtable hashtable = new Hashtable<>();
start = System.nanoTime();
for (int i = 0; i < size; i++) {
hashtable.put(i, "Value" + i);
}
long tablePutTime = System.nanoTime() - start;
start = System.nanoTime();
for (int i = 0; i < size; i++) {
hashtable.get(i);
}
long tableGetTime = System.nanoTime() - start;
System.out.println("HashMap put: " + hashPutTime / 1_000_000 + " ms");
System.out.println("Hashtable put: " + tablePutTime / 1_000_000 + " ms");
System.out.println("HashMap get: " + hashGetTime / 1_000_000 + " ms");
System.out.println("Hashtable get: " + tableGetTime / 1_000_000 + " ms");
System.out.println("HashMap is " + (tablePutTime/hashPutTime) + "x faster for put");
}
}
Thread-Safety Test
public class ThreadSafetyTest {
public static void main(String[] args) throws InterruptedException {
// HashMap - NOT thread-safe (produces inconsistent results)
Map hashMap = new HashMap<>();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
hashMap.put(i, i);
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("HashMap size (may be incorrect): " + hashMap.size());
// Could be less than 2000 due to race conditions
// Hashtable - Thread-safe
Hashtable hashtable = new Hashtable<>();
Runnable safeTask = () -> {
for (int i = 0; i < 1000; i++) {
hashtable.put(i, i);
}
};
t1 = new Thread(safeTask);
t2 = new Thread(safeTask);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Hashtable size (always correct): " + hashtable.size());
// Always outputs 2000
}
}
Comparison Table
| Feature | HashMap | Hashtable |
|---|---|---|
| Synchronized | No (not thread-safe) | Yes (thread-safe) |
| Null keys/values | Allows null key and null values | No nulls (throws NPE) |
| Performance | Faster | Slower (synchronized methods) |
| Iterator | Fail-fast Iterator | Enumeration (legacy) |
| Introduced | Java 1.2 (Collections Framework) | Java 1.0 (Legacy) |
| Inheritance | extends AbstractMap | extends Dictionary |
Modern Alternative: ConcurrentHashMap
import java.util.concurrent.*;
// Best of both worlds - thread-safe + high performance
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap concurrentMap = new ConcurrentHashMap<>();
// Thread-safe and faster than Hashtable
concurrentMap.put("Key1", "Value1");
concurrentMap.put("Key2", "Value2");
// Atomic operations
concurrentMap.putIfAbsent("Key1", "Default");
concurrentMap.replace("Key2", "NewValue");
// Compute if absent
concurrentMap.computeIfAbsent("Key3", k -> "Computed");
System.out.println("ConcurrentHashMap: " + concurrentMap);
// Use ConcurrentHashMap for multi-threaded environments
// Use HashMap for single-threaded (most common case)
// Avoid Hashtable (legacy, use ConcurrentHashMap instead)
}
}
Learn Java Collections best practices with Online Learner!
0
likes
Your Feedback
Help us improve by sharing your thoughts
Online Learner helps developers master programming, database concepts, interview preparation, and real-world implementation through structured learning paths.
Quick Links
© 2023 - 2026 OnlineLearner.in | All Rights Reserved.
