1. Introduction to Hashtable in Java

Hashtable is a legacy collection class in Java that stores information as key-value pairs. It belongs to the java.util package and uses a hash-table-based structure to locate values from their keys.

A Hashtable is notable for two characteristics:

  • It implements the Map interface, so it represents mappings from keys to values.
  • Its public methods are synchronized, meaning individual method calls on the same instance use that instance's monitor lock.

The class was introduced in Java 1.0, before the Collections Framework arrived in Java 1.2. It remains available for compatibility with older code and APIs. For most new concurrent applications, however, ConcurrentHashMap is generally a better fit than Hashtable.

1.1 What does key-value storage mean?

A map associates a key with a value. For example, a student ID can be used as a key to find a student's name. A product code can be used to retrieve a product description, and a username can identify a user record.

Conceptually, a map might contain these mappings:

KeyValue
101Asha
102Rohan
103Meera

A key is used to locate its associated value. Map keys are unique: assigning a new value to an existing key replaces the earlier value for that key.

1.2 Why is Hashtable still taught?

Hashtable is useful to understand because it appears in legacy Java projects, some older APIs, and interview comparisons. Studying it also clarifies the difference between thread-safe individual methods and thread-safe multi-step operations.

Key idea: Hashtable synchronizes individual method calls. That does not automatically make an entire sequence of calls atomic.

2. History and Place in the Collections Framework

Hashtable predates the modern Collections Framework. Like Vector, it originated when Java's early collection classes were designed with synchronization built into their ordinary operations.

When the Collections Framework was introduced in Java 1.2, Hashtable was adapted to implement the Map interface. Its older design choices remained, including synchronized methods and the restriction against null keys and null values.

The class is found in java.util:

  • java.util.Hashtable
  • java.util.Map
  • java.util.HashMap
  • java.util.concurrent.ConcurrentHashMap

Hashtable is not annotated as deprecated simply because newer alternatives exist. It is more accurate to call it a legacy class that is usually not the preferred choice for new code.

2.1 Basic class relationship

Whiteboard
Loading diagram...


The diagram shows that Hashtable and HashMap serve the same broad map role but have different behavior and concurrency characteristics.

3. Important Features of Hashtable

The main characteristics of Hashtable are:

  1. Key-value storage: Each key is associated with a value.
  2. Unique keys: A key cannot represent two separate mappings at once. Reusing a key replaces its value.
  3. Synchronized public methods: Individual operations on the same Hashtable instance acquire its monitor lock.
  4. No null keys: Attempting to insert a null key causes a NullPointerException.
  5. No null values: Attempting to insert a null value also causes a NullPointerException.
  6. No guaranteed iteration order: Hashtable does not promise insertion order or sorted-key order.
  7. Legacy status: It is maintained for compatibility, but newer choices usually fit modern designs better.
  8. Map interface: It offers familiar operations such as put, get, remove, containsKey, and size.

3.1 Default capacity and load factor

Hashtable traditionally uses an initial capacity of 11 and a load factor of 0.75. The initial capacity is a historical design detail; do not treat it as a universal requirement for every map implementation.

The load factor describes how full the table may become before its internal storage is expanded. With a load factor of 0.75, growth is triggered when the table becomes sufficiently occupied according to its implementation's threshold.

Resizing involves allocating a larger internal table and redistributing existing mappings. This work can make a particular insertion more expensive than an ordinary insertion.

4. Creating and Using a Hashtable

Hashtable is created by importing java.util.Hashtable and declaring a variable with the desired key and value types. Generic type parameters make the intended data types clear at compile time.

A typical declaration is conceptually:

Hashtable<String, Integer> scores

This means that keys are expected to be strings and values are expected to be integers.

4.1 Core operations

MethodPurpose
put(key, value)Adds a mapping or replaces the value for an existing key
get(key)Returns the value associated with a key, or null when no mapping is found
remove(key)Removes the mapping for a key
containsKey(key)Checks whether a key is present
containsValue(value)Checks whether a value occurs in the map
size()Returns the number of mappings
isEmpty()Checks whether the map contains no mappings
clear()Removes all mappings
keys()Returns a legacy Enumeration of the keys
elements()Returns a legacy Enumeration of the values
keySet()Returns a set view of the keys
values()Returns a collection view of the values
entrySet()Returns a set view of key-value entries

The exact return types and contracts are defined by the Java API. In everyday code, the Map interface's methods are normally preferred over legacy enumeration methods.

4.2 Replacing an existing value

When put is called with a key that already exists, Hashtable updates the mapping rather than creating a second mapping for that key. The number of mappings therefore does not increase merely because an existing key is assigned a new value.

This is standard Map behavior, not a feature unique to Hashtable.

5. Why Hashtable Rejects Null Keys and Values

Hashtable does not permit null as either a key or a value. An attempt to pass null to put results in a NullPointerException.

This restriction affects code moved between Hashtable and HashMap because HashMap permits one null key and multiple null values.

5.1 The reasoning behind the restriction

If a map permits null values, a call to get(key) that returns null can have two meanings:

  • The key is absent.
  • The key is present and its associated value is null.

In that situation, containsKey(key) is needed to distinguish the two cases.

Hashtable's null restriction avoids that particular ambiguity for stored values: a null result from get indicates that no non-null mapping was returned for that key. Code should still use the appropriate presence-checking method when it needs an explicit test.

5.2 Practical consequence

When migrating code from HashMap to Hashtable, review all paths that may pass null. A program that previously stored a null value can fail at runtime after the map implementation is changed.

This difference should be covered by tests rather than assumed harmless.


6. How Synchronization Works

A synchronized instance method acquires the monitor associated with the current object before executing its body. The monitor is released when the method exits, including when it exits by throwing an exception.

Hashtable synchronizes its public methods. As a result, two threads calling synchronized methods on the same instance cannot execute those method bodies simultaneously while one holds the object's monitor.

6.1 What happens during competing operations?

Imagine Thread A calls put, while Thread B calls get on the same Hashtable. If Thread A acquires the lock first, Thread B waits until Thread A releases it. The read operation also requires the same lock, even though it does not modify the map.

Whiteboard
Loading diagram...


This locking prevents another thread from entering a synchronized Hashtable method on that instance while the current method holds the lock.

6.2 What the lock guarantees

The synchronization protects the map's internal state during an individual synchronized operation. A thread does not enter another synchronized method on the same instance in the middle of the first method's execution.

However, synchronization is not a promise that every larger application-level task is atomic. A sequence such as “check whether a key exists, read its value, subtract one, and write it back” consists of multiple method calls and can be interrupted by another thread between those calls.

6.3 Separate instances have separate locks

The monitor belongs to the particular Hashtable object. Synchronization on one Hashtable does not automatically coordinate access to a different Hashtable instance. Likewise, code that accesses the same underlying data through an unrelated object or unsynchronized reference can require additional care.


7. The Performance Cost of Per-Method Synchronization

A single monitor is simple to reason about, but it can restrict concurrency. If many threads use one Hashtable, they compete for the same lock.

For example, multiple read operations could theoretically be performed concurrently if the data structure and access strategy supported that safely. Hashtable's instance-level synchronization instead serializes its synchronized method calls on that object.

Possible consequences under contention include:

  • Threads spend time waiting to acquire the monitor.
  • Read-heavy workloads cannot freely run many reads in parallel on the same instance.
  • Throughput may stop scaling as thread count increases.
  • A synchronized method may have to wait even when its operation is brief and does not modify the map.

The actual performance impact depends on workload, contention, runtime, and surrounding application logic. It should be measured rather than assumed from the class name alone.

7.1 Synchronization and throughput

Whiteboard
Loading diagram...



8. Why Compound Operations Are Not Automatically Thread-Safe

One of the most important lessons about Hashtable is the difference between atomic individual calls and atomic sequences of calls.

Consider a shared inventory count. The intended operation is to reduce stock by one, but only when the item exists and its count is positive. A naive sequence might:

  1. Check whether the key exists.
  2. Read the current count.
  3. Subtract one.
  4. Store the new count.

Each individual Hashtable method is synchronized, but the lock is released between method calls. Two threads can therefore read the same initial value and both write the same decremented value. One update is lost.

8.1 Lost-update example in principle

Suppose the stock count begins at 10.

StepThread AThread B
1Reads 10—
2—Reads 10
3Writes 9—
4—Writes 9
Final value9

If both decrements had been applied, the final count would be 8. Instead, both threads calculated their update from the same old value, and one decrement was effectively lost.

8.2 Why method synchronization is not enough

The synchronized get call protects only that call. The synchronized put call protects only that call. The map does not automatically hold one continuous lock across the gap between them.

An external synchronized block using the same Hashtable instance as the lock can protect a multi-step sequence, provided all relevant accesses follow the same locking discipline. That solution can be correct but reduces concurrency and requires careful consistency across the codebase.

For new concurrent map logic, higher-level atomic methods such as compute, merge, and putIfAbsent on ConcurrentHashMap are usually easier to use safely for their intended operations.

9. Hashtable Compared with HashMap

Hashtable and HashMap both implement Map and store key-value mappings. Their API shapes overlap substantially, but their null rules and concurrency behavior differ.

FeatureHashtableHashMap
Historical introductionJava 1.0Java 1.2
SynchronizationPublic methods are synchronizedMethods are not synchronized by default
Null keyNot permittedOne null key is permitted
Null valuesNot permittedNull values are permitted
OrderingNo guaranteed orderNo guaranteed order
Typical initial capacity1116 by default in the usual no-argument construction
Typical load factor0.750.75
Concurrent accessIndividual calls are synchronized on the instanceRequires an appropriate concurrency strategy
Modern useMostly legacy compatibilityCommon general-purpose map for non-concurrent use

9.1 The key distinction

It is inaccurate to conclude that Hashtable makes every application-level operation thread-safe simply because its methods are synchronized. Its lock helps protect each individual method call, but a compound read-modify-write sequence still needs an atomic operation or coordinated locking.

Likewise, HashMap should not be treated as safe for unsynchronized concurrent mutation. For concurrent access, choose a design that matches the required behavior rather than relying on accidental timing.

9.2 Choosing between them

Use HashMap for ordinary map storage when external synchronization is not required. Keep Hashtable when compatibility constraints make replacing it risky or unnecessary. For a new shared map with concurrent access, consider ConcurrentHashMap first.

10. Modern Alternative: Collections.synchronizedMap

Collections.synchronizedMap wraps a map in a synchronized view. It can be useful when an existing Map implementation needs synchronized individual method calls.

The wrapper can be used with maps such as HashMap, LinkedHashMap, or TreeMap. The wrapper's method calls use synchronization, but the same general compound-operation limitation applies: a series of calls is not automatically one indivisible operation.

10.1 Iteration requires special attention

When iterating over a synchronized map's collection views, synchronize on the returned synchronized map wrapper for the duration of the iteration, as specified by the Java API. Otherwise, concurrent modifications can interfere with traversal.

This is a common source of bugs because the wrapper synchronizes its individual methods, but an entire iteration spans many operations.

10.2 When it is useful

A synchronized wrapper can be a practical compatibility solution when an existing map implementation is needed and concurrent access is occasional. It is not automatically the highest-throughput choice for a heavily concurrent workload.

11. Modern Alternative: ConcurrentHashMap

ConcurrentHashMap, introduced in Java 5, is designed for concurrent access. It avoids the single-instance-wide method lock strategy used by Hashtable and supports substantially more concurrency.

It is generally the first option to consider when a new application needs a shared map accessed by multiple threads.

11.1 Important properties

  • Supports concurrent access with an implementation designed for that purpose.
  • Does not permit null keys or null values.
  • Provides atomic operations for common compound updates, including putIfAbsent, compute, and merge.
  • Is usually more suitable than Hashtable for workloads with multiple concurrent readers and writers.

The exact internal implementation has evolved across Java versions. Avoid describing every ConcurrentHashMap operation as “lock-free”: the implementation uses different coordination techniques depending on the operation and circumstances. The practical point is that it is designed to allow more concurrency than a single shared monitor.

11.2 Choosing an implementation

RequirementSuitable choice
Ordinary single-threaded map useHashMap
Need a particular insertion-order behaviorLinkedHashMap
Need sorted keysTreeMap
Need concurrent map accessConcurrentHashMap
Need a synchronized wrapper around an existing mapCollections.synchronizedMap(...)
Maintaining a legacy API that specifically expects HashtableHashtable may be required

12. Where Hashtable Still Appears

Although Hashtable is usually not the preferred map for new application code, it remains present in the Java ecosystem.

12.1 Legacy applications

Applications written against older Java versions may use Hashtable throughout their code. Replacing it can be reasonable, but migration needs testing for null behavior, concurrency assumptions, and external API contracts.

12.2 Properties

java.util.Properties extends Hashtable<Object, Object>. Properties is commonly used for configuration data, including values loaded from .properties files. The inheritance relationship is an important reason Hashtable remains visible in modern Java environments.

Although the inheritance exists, application code should normally use the intended Properties API for configuration rather than treating a Properties object as an unrestricted general-purpose Hashtable.

12.3 Older enterprise APIs and JNDI

Some older APIs, including JNDI environment configuration patterns, accept Hashtable values in their method signatures. If an API specifically requires Hashtable, code may need to use it at that boundary even when other map implementations are preferred internally.

13. Common Mistakes and Misunderstandings

13.1 Assuming synchronization makes a whole workflow atomic

Mistake: Believing that get, arithmetic, and put automatically form one protected transaction.

Correction: Each call obtains and releases the lock independently. Use an atomic map operation where available or coordinate the entire sequence under a shared lock.

13.2 Passing null into Hashtable

Mistake: Migrating from HashMap and assuming null keys or values will continue to work.

Correction: Hashtable rejects both. Review all insertion paths and test null-related behavior.

13.3 Assuming Hashtable preserves insertion order

Mistake: Expecting iteration to follow the order in which entries were inserted.

Correction: Hashtable does not guarantee insertion order. Use LinkedHashMap when insertion-order iteration is required.

13.4 Assuming Hashtable is always faster because it is thread-safe

Mistake: Treating synchronization as a performance benefit for every workload.

Correction: A single shared monitor can become a bottleneck. Measure the workload and choose a concurrency-aware collection when needed.

13.5 Calling Hashtable formally deprecated

Mistake: Saying it is deprecated by annotation solely because newer alternatives exist.

Correction: It is commonly considered legacy and generally not recommended for new code, but that is not the same as the class carrying the Java @Deprecated annotation.

13.6 Using a synchronized wrapper without synchronizing iteration

Mistake: Assuming that the wrapper makes a multi-step traversal safe automatically.

Correction: Follow the wrapper's documented iteration synchronization requirements and coordinate modifications consistently.

14. Best Practices

  1. Prefer HashMap for ordinary map storage when concurrent access is not required.
  2. Prefer ConcurrentHashMap for shared maps that need concurrent access.
  3. Use Collections.synchronizedMap when a synchronized wrapper around an existing Map implementation is appropriate.
  4. Keep Hashtable where a legacy contract explicitly requires it, and avoid unnecessary rewrites without testing.
  5. Review null assumptions whenever changing a map implementation.
  6. Use atomic operations such as compute or merge for supported concurrent update patterns.
  7. Treat multi-step workflows as separate from individual synchronized calls.
  8. Follow documented synchronization rules when iterating over synchronized wrappers.
  9. Choose a map based on required semantics—ordering, null handling, concurrency, and API compatibility—not just on class age.
  10. Benchmark real workloads before drawing conclusions about throughput.

15. Decision Flow: Selecting the Right Map

Whiteboard
Loading diagram...


This decision path is a practical starting point, not a replacement for checking the precise API contract and concurrency requirements of an application.

16. Quick Revision Summary

  • Hashtable is a legacy Map implementation introduced in Java 1.0.
  • It is located in java.util.
  • Its public methods are synchronized on the Hashtable instance.
  • It rejects null keys and null values.
  • Its traditional initial capacity is 11, and its load factor is 0.75.
  • It does not guarantee insertion order or sorted-key order.
  • Synchronization protects individual method calls, not arbitrary sequences of calls.
  • A read-modify-write sequence can lose updates when multiple threads interleave.
  • Collections.synchronizedMap synchronizes individual methods on a wrapper, but iteration and compound operations still need care.
  • ConcurrentHashMap is generally the modern choice for concurrent map access.
  • Properties extends Hashtable<Object, Object>.
  • Hashtable is not formally deprecated merely because newer alternatives are preferred.

17. Conclusion

Hashtable is an important legacy Java collection because it illustrates how Java's early collection classes approached synchronization. Its key-value API is familiar, but its built-in single-instance monitor means that method calls on one instance are serialized. That design can limit concurrency, and it does not make a sequence of operations atomic.

The most important practical distinction is between protecting the internal state during an individual method call and protecting the correctness of a larger operation. A synchronized get followed by a calculation and put is still a multi-step workflow unless the entire operation is coordinated.

For new code, choose the map implementation according to the application's requirements. HashMap is suitable for ordinary non-concurrent use, while ConcurrentHashMap is generally preferable for concurrent access. A synchronized wrapper can be useful when an existing map must be exposed through synchronized methods, and Hashtable remains relevant where legacy code or API signatures require it.