In this article, we will discuss ConcurrentHashMap class – the implementation class for ConcurrentMap interface in detail
1. ConcurrentHashMap:
- ConcurrentHashMap is the implementation class of ConcurrentMap interface (i.e.; ConcurrentHashMap implements ConcurrentMap)
- ConcurrentHashMap uses hash table data structure to store key-value pairs (which is known as map entry)
- Allows only unique keys and there is no such restriction on values
- NULL insertion isn’t allowed for both key and values
- Allows concurrent access of read and update operations (i.e.; 2 or more threads can operate on same ConcurrentHashMap object simultaneously)
- For read operation, lock isn’t required
- But for update operation, lock is required but that’s only for part of the Map object (i.e.; Bucket level locking)
- Actually, bucket is divided into n-number of parts and one lock is associated with each part
- These locks are referred as concurrency-level
- ConcurrentHashMap never throws ConcurrentModificationException while 2 or more threads operating simultaneously
- Present in java.util.concurrent package and extends java.util.AbstractMap implements java.util.concurrent.ConcurrentMap interface
- Also, implements java.io.Serializable marker interfaces which provides special ability to ConcurrentHashMap (provided by JVM at run time) like,
- java.io.Serializable: to transfer objects across network
Source: Team BenchResources.Net
2. ConcurrentHashMap constructors:
2.1 ConcurrentHashMap chm = new ConcurrentHashMap();
- creates an empty ConcurrentHashMap object of size 16
- with default fill ratio of 0.75 and default concurrency level 16
2.2 ConcurrentHashMap chs = new ConcurrentHashMap(int initialCapacity);
- creates an empty ConcurrentHashMap object of specified size or initial capacity
- with default fill ratio 0.75 and default concurrency level 16
2.3 ConcurrentHashMap chs = new ConcurrentHashMap(int initialCapacity, float loadFactor);
- creates an empty ConcurrentHashMap object of specified size or initial capacity
- with specified fill ratio (for example 0.85) and default concurrency level 16
2.4 ConcurrentHashMap chs = new ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel);
- creates an empty ConcurrentHashMap object of specified size or initial capacity
- with specified fill ratio (for example 0.85) and specified concurrency level (for example 11)
2.5 ConcurrentHashMap chs = new ConcurrentHashMap(Map m);
- creates an equivalent ConcurrentHashMap object for the specified map
- it is basically used for inter-conversion between map objects
3. Fill ratio (or Load factor) :
- Fill ratio is also known as Load factor
- This factor determines when to increase the size of LinkedHashMap automatically
- For example, for the 1st two constructors the default load factor is 75 –> which means after filling 75 % of HashMap, new HashMap of bigger size will be created
- For 3rd constructor, programmer can define load factor while creating HashMap object. If programmer define it to be 0.95, then after filling 95% of HashMap, size of HashMap will be increased automatically
4. ConcurrentHashMap example:
ConcurrentHashMapPutRemoveAndReplace.java
package in.bench.resources.concurrent.collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapPutRemoveAndReplace {
public static void main(String[] args) {
// creating ConcurrentHashMap object of type <Integer, String>
ConcurrentHashMap<Integer, String> chm =
new ConcurrentHashMap<Integer, String>();
// adding key-value pairs to ConcurrentHashMap object
chm.put(1, "google.com");
chm.put(2, "youtube.com");
chm.put(3, "facebook.com");
// adding key-value pairs using ConcurrentMap method
chm.putIfAbsent(5, "yahoo.com"); // 1st
chm.putIfAbsent(7, "wikipedia.com"); // 2nd
// not-inserted, as key is already present
chm.putIfAbsent(1, "baidu.com"); // 3rd
System.out.println("Iterating before remove and replace\n");
// iterating using enhanced for-loop
for(Map.Entry<Integer, String> me : chm.entrySet()) {
System.out.println("Rank : " + me.getKey() + "\t"
+ "Website : " + me.getValue());
}
// removing: both key and value should match
chm.remove(5, "yahoo.com");
System.out.println("\n\nIterating after remove(5, yahoo.com)\n");
// iterating using enhanced for-loop
for(Map.Entry<Integer, String> me : chm.entrySet()) {
System.out.println("Rank : " + me.getKey() + "\t"
+ "Website : " + me.getValue());
}
// replacing: both key and value should match
chm.replace(2, "youtube.com", "amazon.com");
System.out.println("\n\nIterating after "
+ "replace(2, youtube.com, amazon.com)\n");
// iterating using enhanced for-loop
for(Map.Entry<Integer, String> me : chm.entrySet()) {
System.out.println("Rank : " + me.getKey() + "\t"
+ "Website : " + me.getValue());
}
}
}
Output:
Iterating before remove and replace
Rank : 2 Website : youtube.com
Rank : 1 Website : google.com
Rank : 5 Website : yahoo.com
Rank : 7 Website : wikipedia.com
Rank : 3 Website : facebook.com
Iterating after remove(5, yahoo.com)
Rank : 2 Website : youtube.com
Rank : 1 Website : google.com
Rank : 7 Website : wikipedia.com
Rank : 3 Website : facebook.com
Iterating after replace(2, youtube.com, amazon.com)
Rank : 2 Website : amazon.com
Rank : 1 Website : google.com
Rank : 7 Website : wikipedia.com
Rank : 3 Website : facebook.com
Explanation:
- When normal put() method is used to insert key-value pairs, then all entries inserted successfully
- But when putIfAbsent() method of ConcurrentMap is used, then key-value pair is inserted only when key isn’t already present inside ConcurrentHashMap (check comments 1st, 2nd and 3rd)
- As an example in the above case, 1-baidu.com isn’t inserted because 1-google.com is already present in the ConcurrentHashMap
- Similarly, when we use remove(5, yahoo.com) –> this is removed from ConcurrentHashMap only when both key-value pair is present
- Otherwise, no effects take place in the invoking Map
- Likewise, replace(2, youtube.com, amazon.com) method –> 2nd parameter is replaced by 3rd parameter only when matching key-value pair is present in the invoking Map
- Otherwise, no effect take place in the invoking Map
4.1 Modification while Iterating:
- entrySet() method returns set view of map entries
- Iterating ConcurrentHashMap using Iterator interface or enhanced for-loop guarantees that it runs through all entries
- But doesn’t guarantees any modification to iterating map entries
- So, while iterating map entries either we get updated entries or else entries before updation
- See below for Java doc statement highlighted in RED
- Java doc for ConcurrentHashMap in 1.7 version
Related Articles:
- Java 5 – Introduction to Concurrent Collection
- Java 5 – ConcurrentMap interface
- Java 5 – ConcurrentHashMap class with example
- Java 5 – ConcurrentHashMap with Read and Update operations simultaneously
- Java 5 – HashMap v/s ConcurrentHashMap
- Java 5 – ConcurrentHashMap v/s SynchronizedMap
- Java 5 – Concurrent Collection Interview question and answers
References:
- https://docs.oracle.com/javase/tutorial/collections/intro/
- https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html
- https://docs.oracle.com/javase/7/docs/api/java/util/Collection.html
- https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html
- https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html
- https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html
- http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html#entrySet()
Happy Coding !!
Happy Learning !!