Stage 5: Collections compared, lesson 3 of 8

HashMap vs Hashtable

Intermediate3 min read@since 8Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Hashtable is from Java 1.0; HashMap arrived with the Collections framework in Java 1.2. Today Hashtable is legacy:

  • Every method is synchronized on the whole table. One thread at a time, even for reads, so it's slow when several threads use it.
  • No null keys or values (NullPointerException).
  • It still offers old-style Enumerations and extends the obsolete Dictionary class.
  • It never gained HashMap's modern internals, such as tree buckets.

HashMap isn't synchronized, allows one null key and null values, and is the right choice in single-threaded code. When several threads share a map, use ConcurrentHashMap, never Hashtable: it's thread-safe and much faster under concurrency.

Side by side

HashMapHashtable
IntroducedJava 1.2 (Collections framework)Java 1.0 (legacy)
Thread-safeNoYes: every method synchronized
PerformanceFastSlow under contention (one lock for everything)
null keys / valuesOne null key, any number of null valuesNeither: NullPointerException
IterationFail-fast IteratorEnumeration (not fail-fast) or fail-fast Iterator
SuperclassAbstractMapDictionary (obsolete)
Crowded bucketsList, then tree (Java 8+)List only
Default capacity16 (power of two)11
Use todaySingle-threaded codeDon't: use ConcurrentHashMap

Example

Java
Map<String, Integer> hm = new HashMap<>();
hm.put(null, 1);                     // fine
hm.put("a", null);                   // fine

Map<String, Integer> ht = new Hashtable<>();
// ht.put(null, 1);                  // NullPointerException
// ht.put("a", null);                // NullPointerException

// Even a synchronized map doesn't make "check then act" safe:
if (!ht.containsKey("visits")) {     // another thread can put() right here...
    ht.put("visits", 1);             // ...and this overwrites it
}
ht.putIfAbsent("visits", 1);         // one atomic call: correct

Common mistake

Choosing Hashtable "for thread safety". It's slower than ConcurrentHashMap and still doesn't make multi-step operations safe.

Under the hood

Synchronizing each method makes each call atomic, not a sequence of calls. That's why check-then-act code (containsKey then put, get then put) is still broken on Hashtable and Collections.synchronizedMap; use atomic methods such as putIfAbsent, compute and merge on a ConcurrentHashMap instead.

Check yourself

Which of these accepts a null key?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of Java 8 and collections, practically.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.