ConcurrentHashMap vs HashTable vs Collections.synchronizedMap() in Java
This post will discuss the major difference between ConcurrentHashMap and HashTable classes, and Collections.synchronizedMap() method in Java.
1. Overview of ConcurrentHashMap class
The ConcurrentHashMap class, as the name suggests, is designed to support concurrency and high performance in multithreaded applications. It allows multiple threads to read and write to the map concurrently without blocking or locking them. It implements the ConcurrentMap interface, which extends the Map interface, and uses a hash table as the underlying data structure to store key-value pairs.
It works by dividing the map into several segments or buckets based on the hash code of the keys, where each segment has its own lock that controls the access and modification of the key-value pairs in that segment. By default, ConcurrentHashMap has 16 segments, which means that it can support up to 16 concurrent write operations at a time. However, this number can be changed by specifying the concurrency level parameter in the constructor of ConcurrentHashMap.
ConcurrentHashMap also provides some atomic operations that can be performed on the map without locking or synchronization, such as putIfAbsent(), replace(), remove(), and compute(). These operations are useful for implementing common patterns such as check-and-act, compare-and-swap, and compute-if-absent. Here is an example of how to create and use a ConcurrentHashMap in Java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; class Main { public static void main(String[] args) { // Create a ConcurrentHashMap object Map<Integer, String> chm = new ConcurrentHashMap<>(); // Put some key-value pairs in the map chm.put(1, "Java"); chm.put(2, "Python"); chm.put(3, "C++"); System.out.println(chm); // {1=Java, 2=Python, 3=C++} /* Perform some atomic operations on the map */ // Put a new key-value pair if the key does not exist chm.putIfAbsent(4, "Ruby"); // Replace an existing value with a new value if the old value matches chm.replace(2, "Python", "JavaScript"); // Remove a key-value pair if both the key and value match chm.remove(3, "C++"); System.out.println(chm); // {1=Java, 2=JavaScript, 4=Ruby} } } |
2. Overview of HashTable class
Hashtable is a legacy class that implements the Map interface and uses a hash table as the underlying data structure to store key-value pairs. It is similar to HashMap in many aspects, but it is synchronized by default. Every method in HashTable is synchronized, which means that only one thread can access or modify the table at a time. This ensures that HashTable is consistent and coherent across all threads, but also reduces its concurrency and performance. Here is an example of how to create and use a HashTable in Java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import java.util.Hashtable; import java.util.Map; class Main { public static void main(String[] args) { // Create a HashTable object Map<Integer, String> htab = new Hashtable<>(Map.of(1, "Java", 2, "Python", 3, "C++")); System.out.println(htab); // {3=C++, 2=Python, 1=Java} // Modify the table htab.put(4, "Ruby"); // Replace an existing value with a new value htab.replace(2, "JavaScript"); // Remove a key-value pair from the table htab.remove(3); System.out.println(htab); // {4=Ruby, 2=JavaScript, 1=Java} } } |
3. Overview of Collections.synchronizedMap() method
The Collections.synchronizedMap() method creates a thread-safe or synchronized map from an existing map, such as HashMap or TreeMap or LinkedHashMap. It takes a map object as a parameter and returns a synchronized (thread-safe) map backed by the specified map. It uses a single lock to control the access and modification of the entire map. This means that only one thread can read or write to the map at a time, while other threads have to wait until the lock is released. This ensures that the map is consistent and coherent across all threads, but also reduces the concurrency and performance of the map.
Here is an example of how to create and use a Collections.synchronizedMap() in Java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import java.util.Collections; import java.util.HashMap; import java.util.Map; class Main { public static void main(String[] args) { // Create a HashMap object Map<Integer, String> hmap = new HashMap<>(Map.of(1, "Java", 2, "Python", 3, "C++")); // Create a Collections.synchronizedMap() from the existing map Map<Integer, String> smap = Collections.synchronizedMap(hmap); System.out.println(smap); // {1=Java, 2=Python, 3=C++} // Put a new key-value pair in the synchronized map smap.put(4, "Ruby"); // Replace an existing value with a new value smap.replace(2, "JavaScript"); // Remove a key-value pair from the synchronized map smap.remove(3); System.out.println(smap); // {1=Java, 2=JavaScript, 4=Ruby} // Iterate over the synchronized map using a synchronized block synchronized (smap) { for (Map.Entry<Integer, String> entry : smap.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); } } // Key: 1, Value: Java // Key: 2, Value: JavaScript // Key: 4, Value: Ruby } } |
4. Difference between ConcurrentHashMap, HashTable, and Collections.synchronizedMap()
Here are some differences between ConcurrentHashMap class, Collections.synchronizedMap() method, and HashTable class in Java:
1. Concurrency
ConcurrentHashMapis a concurrent version of the standardHashMapclass, which allows the concurrent modification of a map by multiple threads. It works by locking the bucket which is being updated while other buckets are free to be accessed by other threads.- Unlike
ConcurrentHashMap,Collections.SynchronizedMaplocks the entire table, blocking parallel access for multiple threads. In other words, only one thread can access the map, which ultimately leads to poor performance. - Like
ConcurrentHashMap,Hashtableimplementation is also synchronized, but it acquires the lock on the entire map instance.
2. Null values
ConcurrentHashMap and Hashtable does not permit null keys or values. If we try to insert a null key or value, we will get a NullPointerException.
- The behavior of
Collections.SynchronizedMap()depends on the backing map. If null keys and values are required,HashMapcan be used as the backing set.
3. Fail-safe or Fail-fast behavior
- The iterator returned by the
ConcurrentHashMapis weakly consistent, which never throwsConcurrentModificationExceptioneven if the map is modified after the construction of the iterator. It makes no guarantees to reflect any modifications after its construction. Collections.SynchronizedMap()does not guarantee a fail-safe iterator on concurrent modification (one thread is updating the map, and another thread is traversing the map using an iterator). The fail-safe or fail-fast behavior totally depends on the backing map.- The iterator returned by the
Hashtableclass is fail-fast and throws aConcurrentModificationExceptionif the map is structurally modified after creating the iterator.Hashtableis also traversed byEnumerator, which is fail-safe. The results are undefined if theHashtableis structurally modified after the enumeration is created.
4. Iteration Order
ConcurrentHashMapandHashTabledo not preserve the insertion order of mappings in the map. Also, the addition and removal of any element might change its iteration order.Collections.SynchronizedMap()is backed by the specified map and retains the insertion order of the map. IfHashMapis passed to it, the order of the map is undefined, and if aTreeMapis used, the order is the same as the natural ordering of its keys or that of a provided Comparator.
5. What to use and when?
Here are some general recommendations on choosing between ConcurrentHashMap, HashTable, and Collections.synchronizedMap():
- If you need a map that can handle multiple concurrent write and update operations without blocking or locking threads, we should use
ConcurrentHashMap. It can be used on performance-critical systems, where there are far more update operations on the map than the read operations. - If you need a map that can ensure thread safety and synchronization across all threads, we should use
Collections.synchronizedMap(). It can be used when data consistency is important. - If you need map that can work with older versions of Java or legacy codebases, we should use
HashTable. However, Javadoc recommends usingConcurrentHashMapin place ofHashtablefor thread-safe highly concurrent implementation.
That’s all about the ConcurrentHashMap, HashTable, and Collections.synchronizedMap() in Java.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)