00:00

Hashtable in Java – Complete Guide with Examples

Last updated: 8 min read

Hashtable is a key–value hash table that has been in Java since JDK 1.0, before the Collections Framework existed. Like HashMap, it maps unique keys to values, but every public method is synchronized, and it does not allow null keys or values.

Hashtable was later updated to implement the Map interface. You will meet it in older code, in the Properties class, and in interviews. This tutorial explains how it works, how it differs from HashMap, and what to use instead today.

1. What Is a Hashtable?

  • Key–value storage with unique keys, like every Map.
  • Synchronized: only one thread at a time can call any of its methods.
  • No null keys or values; trying throws NullPointerException.
  • No guaranteed order of entries.
  • Legacy API: extends the old abstract class Dictionary and supports Enumeration.
  • Default capacity 11, load factor 0.75; grows to 2 × old + 1.

2. Class Hierarchy and Declaration

Hashtable class hierarchy in Java
java.util.Hashtable
public class Hashtable<K, V> extends Dictionary<K, V>
        implements Map<K, V>, Cloneable, java.io.Serializable

java.util.Properties extends Hashtable.

3. Your First Hashtable Program

Because Hashtable implements Map, everyday use looks exactly like HashMap:

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

public class HashtableQuickStart {
    public static void main(String[] args) {
        Map<Integer, String> rooms = new Hashtable<>();
        rooms.put(101, "Asha");
        rooms.put(102, "Ravi");
        rooms.put(103, "Meera");

        System.out.println("Rooms:        " + rooms);
        System.out.println("get(102):     " + rooms.get(102));
        System.out.println("has room 103: " + rooms.containsKey(103));

        rooms.put(102, "Kiran");                 // replace the guest
        rooms.remove(101);
        System.out.println("After update: " + rooms + ", size=" + rooms.size());
    }
}
Output
Rooms:        {103=Meera, 102=Ravi, 101=Asha}
get(102):     Ravi
has room 103: true
After update: {103=Meera, 102=Kiran}, size=2

4. Constructors

ConstructorWhat it does
Hashtable()Empty table, capacity 11, load factor 0.75.
Hashtable(int initialCapacity)Empty table with the given capacity.
Hashtable(int initialCapacity, float loadFactor)Empty table with the given capacity and load factor.
Hashtable(Map<? extends K, ? extends V> t)A table with the same entries as t.

5. Why Hashtable Rejects null

Hashtable calls key.hashCode() directly, which fails for a null key, and it checks values explicitly. There is also a design reason: in a shared, synchronized map, get() returning null must clearly mean "no entry", not "entry with a null value".

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

public class HashtableNulls {
    static void tryPut(String label, Map<String, String> map, String k, String v) {
        try {
            map.put(k, v);
            System.out.println(label + " put(" + k + ", " + v + ") -> ok");
        } catch (NullPointerException e) {
            System.out.println(label + " put(" + k + ", " + v + ") -> NullPointerException");
        }
    }

    public static void main(String[] args) {
        tryPut("HashMap:  ", new HashMap<>(), null, "x");
        tryPut("HashMap:  ", new HashMap<>(), "k", null);
        tryPut("Hashtable:", new Hashtable<>(), null, "x");
        tryPut("Hashtable:", new Hashtable<>(), "k", null);
    }
}
Output
HashMap:   put(null, x) -> ok
HashMap:   put(k, null) -> ok
Hashtable: put(null, x) -> NullPointerException
Hashtable: put(k, null) -> NullPointerException

6. Legacy Methods and Enumeration

Hashtable keeps its JDK 1.0 methods: keys() and elements() return an Enumeration, and contains(value) checks values, not keys, which is a common source of confusion.

HashtableEnumeration.java
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TreeMap;

public class HashtableEnumeration {
    public static void main(String[] args) {
        Hashtable<String, Integer> ages = new Hashtable<>();
        ages.put("Asha", 31);
        ages.put("Ravi", 27);
        ages.put("Meera", 35);

        // Legacy style: Enumeration over keys and over values
        Enumeration<String> keys = ages.keys();
        int count = 0;
        while (keys.hasMoreElements()) {
            keys.nextElement();
            count++;
        }
        System.out.println("Keys counted with keys(): " + count);

        int total = 0;
        Enumeration<Integer> values = ages.elements();
        while (values.hasMoreElements()) total += values.nextElement();
        System.out.println("Sum of ages with elements(): " + total);

        // Legacy contains() checks VALUES (same as containsValue)
        System.out.println("contains(27): " + ages.contains(27));

        // Modern style works too, because Hashtable is a Map
        ages.forEach((name, age) -> { if (age > 30) System.out.println(name + " is over 30"); });
        System.out.println("Sorted: " + new TreeMap<>(ages));
    }
}
Output
Keys counted with keys(): 3
Sum of ages with elements(): 93
contains(27): true
Meera is over 30
Asha is over 30
Sorted: {Asha=31, Meera=35, Ravi=27}
Legacy methodModern Map equivalent
keys()keySet().iterator()
elements()values().iterator()
contains(value)containsValue(value)
rehash() (protected)Automatic resizing

7. Thread Safety in Hashtable

Each method, including merge() and compute(), is synchronized on the Hashtable object. Two threads counting clicks therefore get the right total:

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

public class HashtableThreads {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> counter = new Hashtable<>();

        Runnable task = () -> {
            for (int i = 0; i < 50_000; i++) {
                counter.merge("clicks", 1, Integer::sum);   // merge is synchronized
            }
        };
        Thread t1 = new Thread(task), t2 = new Thread(task);
        t1.start(); t2.start();
        t1.join();  t2.join();

        System.out.println("Total clicks: " + counter.get("clicks"));
    }
}
Output
Total clicks: 100000

The downsides:

  • One lock for everything: while one thread reads, all others wait, even for different keys.
  • Compound actions still need care: if (!t.containsKey(k)) t.put(k, v); is two separate calls; use putIfAbsent() or synchronize on the table.
  • Iteration needs a manual lock: hold synchronized (table) for the whole loop.

8. How Hashtable Works Internally

Hashtable is an array of buckets, each holding a linked list of entries. For a key, it calculates index = (hash & 0x7FFFFFFF) % table.length, then searches that bucket with equals(). Differences from modern HashMap:

  • It uses modulo with an odd table size (11, 23, 47 …) instead of a power of two.
  • It does not spread the hash bits.
  • Buckets never turn into trees, so many collisions mean O(n) lookups.
  • The table is created immediately, not lazily.

9. Properties: Hashtable's Best-Known Subclass

Properties stores configuration as string key–value pairs and can load them from .properties files. It extends Hashtable, which is why it is thread-safe.

PropertiesDemo.java
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;

public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
        String config = """
                app.name=Shop
                app.port=8080
                # comments are ignored
                """;

        Properties props = new Properties();     // Properties extends Hashtable<Object, Object>
        props.load(new StringReader(config));

        System.out.println("Name:    " + props.getProperty("app.name"));
        System.out.println("Port:    " + Integer.parseInt(props.getProperty("app.port")));
        System.out.println("Timeout: " + props.getProperty("app.timeout", "30s") + " (default)");
    }
}
Output
Name:    Shop
Port:    8080
Timeout: 30s (default)

Use getProperty() and setProperty() rather than get() and put(), so that only strings are stored.

10. Common Hashtable Methods

MethodDescription
put(k, v), get(k), remove(k)Basic map operations (synchronized).
containsKey(k), containsValue(v), contains(v)Check for a key or a value.
keys(), elements()Legacy enumerations of keys and values.
keySet(), values(), entrySet()Synchronized collection views.
putIfAbsent, merge, compute, getOrDefaultJava 8 methods, also synchronized.
size(), isEmpty(), clear()Count, check and empty.
clone()Shallow copy.

11. Hashtable vs HashMap vs ConcurrentHashMap

FeatureHashtableHashMapConcurrentHashMap
IntroducedJDK 1.0JDK 1.2Java 5
Thread-safeYes, one lockNoYes, fine-grained
null key / valuesNo / NoOne / YesNo / No
Default capacity111616
Growth2n + 12n2n
IterationEnumeration and fail-fast IteratorFail-fast IteratorWeakly consistent Iterator
Parent classDictionaryAbstractMapAbstractMap

12. What to Use Instead

  • Single-threaded code: HashMap.
  • Shared between threads: ConcurrentHashMap, which scales much better.
  • Need a synchronized wrapper around any map: Collections.synchronizedMap().
  • Configuration: Properties is still fine.

Hashtable is not deprecated, but the JDK documentation itself recommends HashMap or ConcurrentHashMap for new code.

13. Summary

  • Hashtable is a legacy, synchronized hash table that implements Map.
  • It does not allow null keys or values.
  • Default capacity is 11, and it grows to 2n + 1.
  • It supports old Enumeration methods; contains() checks values.
  • Properties extends it; for new code use HashMap or ConcurrentHashMap.

Related Tutorials

Reference: Java SE 21 API: java.util.Hashtable