What are the differences between a HashMap and a Hashtable in Java?
Which is more efficient for non-threaded applications?
A Collection — sometimes called a container — is simply an object that groups multiple elements into a single unit. Collection
s are used to store, retrieve, manipulate, and communicate aggregate data. A collections framework W is a unified architecture for representing and manipulating collections.
The HashMap
JDK1.2 and Hashtable JDK1.0, both are used to represent a group of objects that are represented in
pair. Each
pair is called Entry
object. The collection of Entries is referred by the object of HashMap
and Hashtable
. Keys in a collection must be unique or distinctive. [as they are used to retrieve a mapped value a particular key. values in a collection can be duplicated.]
« Superclass, Legacy and Collection Framework member
Hashtable is a legacy class introduced in JDK1.0
, which is a subclass of Dictionary class. From JDK1.2
Hashtable is re-engineered to implement the Map interface to make a member of collection framework. HashMap is a member of Java Collection Framework right from the beginning of its introduction in JDK1.2
. HashMap is the subclass of the AbstractMap class.
public class Hashtable extends Dictionary implements Map, Cloneable, Serializable { ... }
public class HashMap extends AbstractMap implements Map, Cloneable, Serializable { ... }
« Initial capacity and Load factor
The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. Note that the hash table is open: in the case of a "hash
collision", a single bucket stores multiple entries, which must be searched sequentially. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.
HashMap constructs an empty hash table with the default initial capacity (16) and the default load factor (0.75). Where as Hashtable constructs empty hashtable with a default initial capacity (11) and load factor/fill ratio (0.75).
« Structural modification in case of hash collision
HashMap
, Hashtable
in case of hash collisions they store the map entries in linked lists. From Java8 for HashMap
if hash bucket grows beyond a certain threshold, that bucket will switch from linked list of entries to a balanced tree. which improve worst-case performance from O(n) to O(log n). While converting the list to binary tree, hashcode is used as a branching variable. If there are two different hashcodes in the same bucket, one is considered bigger and goes to the right of the tree and other one to the left. But when both the hashcodes are equal, HashMap
assumes that the keys are comparable, and compares the key to determine the direction so that some order can be maintained. It is a good practice to make the keys of HashMap
comparable. On adding entries if bucket size reaches TREEIFY_THRESHOLD = 8
convert linked list of entries to a balanced tree, on removing entries less than TREEIFY_THRESHOLD
and at most UNTREEIFY_THRESHOLD = 6
will reconvert balanced tree to linked list of entries. Java 8 SRC, stackpost
« Collection-view iteration, Fail-Fast and Fail-Safe
+--------------------+-----------+-------------+
| | Iterator | Enumeration |
+--------------------+-----------+-------------+
| Hashtable | fail-fast | safe |
+--------------------+-----------+-------------+
| HashMap | fail-fast | fail-fast |
+--------------------+-----------+-------------+
| ConcurrentHashMap | safe | safe |
+--------------------+-----------+-------------+
Iterator is a fail-fast in nature. i.e it throws ConcurrentModificationException if a collection is modified while iterating other than it’s own remove() method. Where as Enumeration is fail-safe in nature. It doesn’t throw any exceptions if a collection is modified while iterating.
According to Java API Docs, Iterator is always preferred over the Enumeration.
NOTE: The functionality of Enumeration interface is duplicated by the Iterator interface. In addition, Iterator adds an optional remove operation, and has shorter method names. New implementations should consider using Iterator in preference to Enumeration.
In Java 5 introduced ConcurrentMap Interface: ConcurrentHashMap
- a highly concurrent, high-performance ConcurrentMap
implementation backed by a hash table. This implementation never blocks when performing retrievals and allows the client to select the concurrency level for updates. It is intended as a drop-in replacement for Hashtable
: in addition to implementing ConcurrentMap
, it supports all of the "legacy" methods peculiar to Hashtable
.
Each HashMapEntry
s value is volatile thereby ensuring fine grain consistency for contended modifications and subsequent reads; each read reflects the most recently completed update
Iterators and Enumerations are Fail Safe - reflecting the state at some point since the creation of iterator/enumeration; this allows for simultaneous reads and modifications at the cost of reduced consistency. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time.
Like Hashtable
but unlike HashMap
, this class does not allow null to be used as a key or value.
public static void main(String[] args) {
//HashMap hash = new HashMap();
Hashtable hash = new Hashtable();
//ConcurrentHashMap hash = new ConcurrentHashMap<>();
new Thread() {
@Override public void run() {
try {
for (int i = 10; i < 20; i++) {
sleepThread(1);
System.out.println("T1 :- Key"+i);
hash.put("Key"+i, i);
}
System.out.println( System.identityHashCode( hash ) );
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override public void run() {
try {
sleepThread(5);
// ConcurrentHashMap traverse using Iterator, Enumeration is Fail-Safe.
// Hashtable traverse using Enumeration is Fail-Safe, Iterator is Fail-Fast.
for (Enumeration e = hash.keys(); e.hasMoreElements(); ) {
sleepThread(1);
System.out.println("T2 : "+ e.nextElement());
}
// HashMap traverse using Iterator, Enumeration is Fail-Fast.
/*
for (Iterator< Entry > it = hash.entrySet().iterator(); it.hasNext(); ) {
sleepThread(1);
System.out.println("T2 : "+ it.next());
// ConcurrentModificationException at java.util.Hashtable$Enumerator.next
}
*/
/*
Set< Entry > entrySet = hash.entrySet();
Iterator< Entry > it = entrySet.iterator();
Enumeration> entryEnumeration = Collections.enumeration( entrySet );
while( entryEnumeration.hasMoreElements() ) {
sleepThread(1);
Entry nextElement = entryEnumeration.nextElement();
System.out.println("T2 : "+ nextElement.getKey() +" : "+ nextElement.getValue() );
//java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode
// at java.util.HashMap$EntryIterator.next
// at java.util.Collections$3.nextElement
}
*/
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
Map unmodifiableMap = Collections.unmodifiableMap( map );
try {
unmodifiableMap.put("key4", "unmodifiableMap");
} catch (java.lang.UnsupportedOperationException e) {
System.err.println("UnsupportedOperationException : "+ e.getMessage() );
}
}
static void sleepThread( int sec ) {
try {
Thread.sleep( 1000 * sec );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
« Null Keys And Null Values
HashMap
allows maximum one null key and any number of null values. Where as Hashtable
doesn’t allow even a single null key and null value, if the key or value null is then it throws NullPointerException. Example
« Synchronized, Thread Safe
Hashtable
is internally synchronized. Therefore, it is very much safe to use Hashtable
in multi threaded applications. Where as HashMap
is not internally synchronized. Therefore, it is not safe to use HashMap
in multi threaded applications without external synchronization. You can externally synchronize HashMap
using Collections.synchronizedMap()
method.
« Performance
As Hashtable
is internally synchronized, this makes Hashtable
slightly slower than the HashMap
.
@See