JavaCollectionsintermediate
Updated:

HashMap vs Hashtable: Key Differences

6 min read

Every difference between HashMap and Hashtable that matters — synchronization, nulls, iterators, performance — and why new code should use neither for concurrency.

TL;DR – Quick Answer

HashMap is unsynchronized, allows one null key and null values, and is the standard choice for single-threaded use. Hashtable is a legacy Java 1.0 class where every method is synchronized, nulls are forbidden, and performance suffers. For thread safety in new code, skip both and use ConcurrentHashMap.

On This Page

"Difference between HashMap and Hashtable?" has been a Java interview staple for two decades. The twist most candidates miss: the best answer ends with "…and in new code I would use neither for concurrency — I would use ConcurrentHashMap." This article gives you the full comparison and that finishing move.

Verdict at a glance

Dimension HashMap Hashtable
Since Java 1.2 (Collections Framework) Java 1.0 (legacy)
Synchronization none every method synchronized
Thread-safe no yes (coarse-grained)
Null key one allowed not allowed (NPE)
Null values allowed not allowed (NPE)
Iterator fail-fast Iterator Iterator + legacy Enumeration
Performance fast slow under any contention
Parent class AbstractMap Dictionary (obsolete)
Treeification (Java 8+) yes no
Modern replacement ConcurrentHashMap

Verdict: use HashMap for single-threaded or externally synchronized code, ConcurrentHashMap for shared mutable state, and Hashtable only when maintaining code that already depends on it.

Origin story: why both exist

Hashtable shipped with Java 1.0 in 1996, before the Collections Framework existed. It extends the obsolete Dictionary class and made every method synchronized because early Java leaned heavily on threads.

Java 1.2 introduced the Collections Framework with a different philosophy: collections are unsynchronized by default, and you add thread safety only where needed. HashMap was the clean-slate redesign. Hashtable was retrofitted to implement Map so old code kept compiling, but it has been a legacy citizen ever since — the JDK's own Javadoc points users to HashMap and ConcurrentHashMap.

Difference 1: synchronization and what it really buys

Every public method of Hashtableget, put, size, all of them — locks the entire table on this. Two consequences:

It is slow. Threads serialize on a single lock even when they only read. On a modern multi-core machine, that lock becomes the bottleneck long before the hashing does.

It is less safe than it looks. Individual calls are atomic, but real code composes calls — and compositions are not protected:

import java.util.Hashtable;
import java.util.Map;

public class CheckThenActRace {
    private static final Map<String, Integer> seats = new Hashtable<>();

    public static void main(String[] args) throws InterruptedException {
        seats.put("A1", 0);

        Runnable book = () -> {
            // check-then-act: NOT atomic even on a Hashtable
            if (seats.get("A1") == 0) {
                try { Thread.sleep(10); } catch (InterruptedException e) { }
                seats.put("A1", 1);
                System.out.println(Thread.currentThread().getName()
                        + " booked seat A1");
            }
        };
        Thread t1 = new Thread(book, "user-1");
        Thread t2 = new Thread(book, "user-2");
        t1.start(); t2.start();
        t1.join(); t2.join();   // frequently BOTH threads "book" the seat
    }
}

Run it a few times: both threads regularly pass the get() check before either writes, and the seat gets double-booked. The synchronized methods did not help, because the race spans two calls. ConcurrentHashMap fixes this class of bug with atomic compound operations like putIfAbsent() and compute() — see HashMap vs ConcurrentHashMap.

Interview note: This is the trap hidden inside "Hashtable is thread-safe." Method-level atomicity does not give you transaction-level atomicity. Pointing that out — ideally with the check-then-act example — moves your answer from "memorized" to "understood".

Difference 2: null handling

HashMap permits one null key (stored in bucket 0) and any number of null values. Hashtable throws NullPointerException for both:

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class NullHandling {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put(null, "null key is fine");
        hashMap.put("config", null);
        System.out.println(hashMap.get(null));   // null key is fine

        Map<String, String> table = new Hashtable<>();
        table.put("ok", "value");
        try {
            table.put(null, "boom");
        } catch (NullPointerException e) {
            System.out.println("Hashtable rejected null key");
        }
        try {
            table.put("key", null);
        } catch (NullPointerException e) {
            System.out.println("Hashtable rejected null value");
        }
    }
}

The null-value ban is not arbitrary. In a concurrent map, get(k) returning null is ambiguous — absent key or stored null? Single-threaded code can disambiguate with containsKey(), but in a concurrent map another thread may change the answer between the two calls. ConcurrentHashMap bans nulls for exactly the same reason.

Difference 3: iteration and fail-fast behavior

HashMap iterators are fail-fast: structurally modify the map mid-iteration (outside Iterator.remove()) and you get a ConcurrentModificationException on a best-effort basis.

Hashtable offers the same fail-fast Iterator through its collection views, plus the pre-collections Enumeration via keys() and elements() — which is not fail-fast and can yield inconsistent views mid-modification. Code still using Enumeration is a reliable sign of a very old codebase.

Difference 4: performance and internals

Beyond locking, the implementations diverged over the years:

  • Java 8 treeification: HashMap converts collision chains longer than 8 nodes into Red-Black trees, capping worst-case lookup at O(log n). Hashtable never got this upgrade — a flood of colliding keys degrades it to O(n) scans. The mechanics are covered in how HashMap works internally.
  • Capacity strategy: HashMap uses power-of-two capacities (default 16) with (n - 1) & hash indexing. Hashtable defaults to 11, grows to 2n + 1, and computes (hash & 0x7FFFFFFF) % capacity — a division on every access.
  • Hash spreading: HashMap mixes high bits into low bits (h ^ (h >>> 16)); Hashtable uses the raw hashCode().

Uncontended, HashMap is simply the faster and more robust design. Contended, Hashtable collapses because every thread queues on one lock.

Common mistake: A common mistake beginners make is "fixing" a race condition by swapping HashMap for Hashtable. It hides the crash but keeps the logic race (as the seat-booking demo shows) and throttles throughput. The honest fix is ConcurrentHashMap with atomic operations, or an explicit lock around the whole business operation.

What about Collections.synchronizedMap()?

Collections.synchronizedMap(new HashMap<>()) is essentially a modern-flavored Hashtable: a wrapper that synchronizes every call on a single mutex. Same coarse lock, same compound-operation problem, and you must manually synchronize during iteration. It exists for adapting legacy single-lock designs, not as a recommendation. For real concurrency the ladder is: HashMap (single thread) → ConcurrentHashMap (shared state). Hashtable and synchronizedMap sit on a rung nobody should climb to in new code.

Choose HashMap if / choose Hashtable if

Choose HashMap if:

  • The map is confined to one thread, or guarded by your own higher-level lock.
  • You need null values (e.g., caching "looked up, found nothing").
  • You care about performance — which is nearly always.
  • You are writing any new code. Pair it with the best practices for keys and capacity.

Choose Hashtable if:

  • You are maintaining legacy code whose API exposes Hashtable or Dictionary and refactoring is out of scope.
  • A legacy library (old Properties-style APIs — java.util.Properties itself extends Hashtable) forces it on you.

That second list is deliberately short. There is no performance, safety or feature scenario where Hashtable beats both HashMap and ConcurrentHashMap.

For freshers: how to answer this in an interview

A tight 30-second answer: "Both implement Map with a hash table. Hashtable is the legacy Java 1.0 class — every method is synchronized, nulls are forbidden, and it's slow under contention. HashMap is the Java 1.2 redesign — unsynchronized, allows one null key and null values, fail-fast iterators, and since Java 8 it treeifies long collision chains. In new code I'd use HashMap single-threaded and ConcurrentHashMap for shared state; Hashtable only survives for backward compatibility."

Expect these follow-ups, in rough order of likelihood: why nulls are banned in concurrent maps, why "thread-safe" doesn't make check-then-put safe, and how ConcurrentHashMap locks differently. The first two are answered above; the third has its own dedicated comparison. For a broader drill across the whole framework, work through the Java collections interview questions set.

Frequently Asked Questions

What is the main difference between HashMap and Hashtable?
Synchronization. Every public method of Hashtable is synchronized, making it thread-safe but slow, while HashMap is unsynchronized and fast. HashMap also permits one null key and any number of null values, both of which Hashtable rejects with a NullPointerException.
Is Hashtable deprecated in Java?
Not formally — it carries no @Deprecated annotation and still ships in every JDK. It is considered obsolete, though. The Javadoc itself recommends HashMap for non-threaded code and ConcurrentHashMap for highly concurrent code. New code has no reason to use Hashtable.
Why does Hashtable not allow null keys or values?
Hashtable calls hashCode() on the key directly, so a null key throws a NullPointerException. Null values are rejected by design: in a concurrent map, get(key) returning null would be ambiguous between key absent and value null, and contains-then-get is not atomic across threads.
Should I use Hashtable for thread safety?
No. Hashtable locks the entire table on every operation, so threads serialize even when reading. ConcurrentHashMap allows fully concurrent reads and fine-grained locking for writes, and provides atomic operations like putIfAbsent and compute. It is the correct choice for concurrent code.
Is compound code like check-then-put safe on a Hashtable?
No. Each individual method is atomic, but sequences are not. Two threads can both see containsKey() return false and then both put, one overwriting the other. You would need external synchronization around the sequence, which defeats the point of using Hashtable.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 14 July 2026 LinkedIn
Chat with us