The java.util.ConcurrentHashMap is one of the most important classes of JDK. It was introduced in JDK 1.5 along with other concurrent collection classes like CopyOnWriteArrayList and BlockingQueue. Ever since then, it has been a workhorse in concurrent Java applications. It won't be an exaggeration If I say there is hardly any concurrent Java application that has not used ConcurrentHashMap yet. The class was another implementation of java.util.Map and a popular hash table data structure with concurrency inbuilt. This means you can also pass a ConcurrentHashMap to a method that is expecting a java.util.Map object. It was made scalable by dividing the whole map into a different segment, known as concurrency level, and then allowing threads to modify segments concurrently.
It also uses other concurrency enhancements like tryLock() to perform some read without locking, you can read more about the internal implementation of a concurrent hash map in my earlier post about how ConcurrentHashMap works in Java.
In this article, I will not talk much about how the concurrent hash map internally implemented but will focus more on how to use the ConcurrentHashMap from an application developer's perspective.
This tutorial is filled with practical examples of day to day task you need to perform, like creating a concurrent HashMap, adding a mapping, retrieving values, updating values atomically, removing a mapping from the map safely, and iterating over concurrent HashMap, going through each entry and removing mapping based upon conditions.
In the process, you will explore almost all important methods of ConcurrentHashMap class like put, get, size, replace, keySet, values, entrySet, clear, isEmpty as well as newly added methods of Java 8 like compute() and merge to update a value for given key atomically, mappingCoung() which return long value, suitable for a large map where a number of keys exceed the maximum limit of int data type in Java.
Btw, if you want to learn Java from scratch then I also recommend you to check The Complete Java Masterclass course on Udemy, one of the most up-to-date courses.
These were the list of tasks you perform day to day with ConcurrentHashMap in most Java applications. Let's see them now.
Also, while creating an object of collection classes using generic, I always like to use diamond operator <> or Java SE 7, which makes me type less code and also look cleaner.
The diamond operator, infer types automatically on the right-hand side, you can read more about it on my post 10 Java SE 7 feature you should learn before starting with Java Se 8, read here.
Once you add an entry, the size of the map is increased by 1, and you can also see mappings by printing the map. It overrides toString() methods and prints both key and value, as shown below:
You can see that initially, the ConcurrentHashMap was empty, and later the "Effective Java" entry was added to it. The put() method returns the value which is mapped to a given key or null if a key is not associated with any value.
It also uses other concurrency enhancements like tryLock() to perform some read without locking, you can read more about the internal implementation of a concurrent hash map in my earlier post about how ConcurrentHashMap works in Java.
In this article, I will not talk much about how the concurrent hash map internally implemented but will focus more on how to use the ConcurrentHashMap from an application developer's perspective.
This tutorial is filled with practical examples of day to day task you need to perform, like creating a concurrent HashMap, adding a mapping, retrieving values, updating values atomically, removing a mapping from the map safely, and iterating over concurrent HashMap, going through each entry and removing mapping based upon conditions.
In the process, you will explore almost all important methods of ConcurrentHashMap class like put, get, size, replace, keySet, values, entrySet, clear, isEmpty as well as newly added methods of Java 8 like compute() and merge to update a value for given key atomically, mappingCoung() which return long value, suitable for a large map where a number of keys exceed the maximum limit of int data type in Java.
Btw, if you want to learn Java from scratch then I also recommend you to check The Complete Java Masterclass course on Udemy, one of the most up-to-date courses.
24 Examples of ConcurrentHashMap in Java 8
So here is my list of some of the useful and practical examples of using ConcurrentHashMap in Java. This will teach you step by step how to use a concurrent hash map in your Java application.- adding a mapping
- retrieving value
- updating value
- removing value
- retrieving all key-value pairs
- getting a set of keys
- getting a collection of values
- checking if a map is empty
- iterating over map
- removing entry during iteration
- atomic update of values
- checking if a key exists in the map
- checking if a value exists in the map
- copying all mapping from one map to other
- compute() example
- merge() example
- getOrDefault() example
- mappingCount() example
These were the list of tasks you perform day to day with ConcurrentHashMap in most Java applications. Let's see them now.
1. How to create a concurrent HashMap in Java
The first step is to create a concurrent HashMap in Java. Since ConcurrentHashMap implements ConcurrentMap, it's advisable to use that interface to store a ConcurrentHashMap object. Still, sometimes you need ConcurrentHashMap type variable because certain methods are only available in ConcurrentHashMap class and not in ConcurrentMap interface like forEachKey(), search(), etc.ConcurrentMap<String, Integer> bookAndPrice = new ConcurrentHashMap<>();
Also, while creating an object of collection classes using generic, I always like to use diamond operator <> or Java SE 7, which makes me type less code and also look cleaner.
The diamond operator, infer types automatically on the right-hand side, you can read more about it on my post 10 Java SE 7 feature you should learn before starting with Java Se 8, read here.
2. How to add a mapping - put() example
The put() method is used to add a key-value pair into a Map, and ConcurrentHashMap is no different, ultimately it is a map, which means you can pass this to any method expecting a java.util.Map or java.util.ConcurrentMap type of variable. Let's add a mapping into concurrent HashMap.bookAndPrice.put("Effective Java", 42);
Once you add an entry, the size of the map is increased by 1, and you can also see mappings by printing the map. It overrides toString() methods and prints both key and value, as shown below:
before : {} after : {Effective Java=42}
You can see that initially, the ConcurrentHashMap was empty, and later the "Effective Java" entry was added to it. The put() method returns the value which is mapped to a given key or null if a key is not associated with any value.
Remember, the ConcurrentHashMap doesn't support null values because null is used to indicate an absence of mapping in the ConcurrentHashMap. Many methods, including get(), return null if there is no value is associated with the key.
3. How to retrieve a value from ConcurrentHashMap - get() example
The get() method is used to retrieve a value from the ConcurrentHashMap in Java. This method returns the value or null if no value is associated with the key.Here is an example of retrieving value from ConcurrentHashMap in Java:
The get() method uses hashCode() of the key object to find values. If you remember, it's important for both key and value objects to implement the equals() and hashcode() method to be stored inside a Map and ConcurrentHashMap is not an exception to that. Ultimately, it is still a Map.
Integer price = bookAndPrice.get("Effective Java");; //return 42;
Integer price2 = bookAndPrice.get("Head First Java"); // return null
4. How to find the number of entries - size() example
The size() method is used to find the number of entries inside a ConcurrentHashMap. This method returns an int value to denote how many mappings are available. Let's see an example of the size method.bookAndPrice.size(); // return 1
The above call returned 1 because there is just one mapping in the bookAndPrice ConcurrentHashMap. Though, it's discouraged to use size() from Java 8 because it returns an int, which is not suitable for a large map, where the number of entries exceeds the maximum value of int like, Integer.MAX_VALUE.Instead, a new method mappingCount() has been added, which returns a long value. See The Complete Java Masterclass to learn more about new methods added into the ConcurrentHashMap class in Java 8.
5. How to replace an existing entry - replace() example
You can replace an existing mapping in a ConcurentHashMap by using the replace() function. The replace() function takes a key, an old value, and a new value and only replaces the value if the old value is the same as the currently mapped value.This is important because ConcurrentHashMap can be updated by multiple threads at the same time, and it's possible that two threads are updating the same value at the same time.
This will replace the price of Effective Java from 48 to 28 only if no other thread has updated the value in the meantime. Suppose, if some thread updated the value from 42 to 32, then this replace() call will not update the value and return false.
The forEach() method is defined in ConcurrentMap interface but other forEach methods like forEachKey() and forEachValues() are defined in the java.util.ConcurrentHashMap class.
bookAndPrice.replace("Effective Java", 42, 28);
This will replace the price of Effective Java from 48 to 28 only if no other thread has updated the value in the meantime. Suppose, if some thread updated the value from 42 to 32, then this replace() call will not update the value and return false.
6. How to iterate over ConcurrentHashMap in Java
There are many ways to iterate over a Map in Java 8 (see here) and those can be used to iterate over ConcurrentHashMap as well. Though, you can also use various forEach() method from ConcurrentHashMap for iteration like forEach(), forEachKey() or forEachValue() as shown below:bookAndPrice.forEach((k, v) -> System.out.println("key: " + k + " value: " + v));
Output
key: Effective Java value: 42
The forEach() method is defined in ConcurrentMap interface but other forEach methods like forEachKey() and forEachValues() are defined in the java.util.ConcurrentHashMap class.
7. How to get all keys from ConcurrentHashMap - use keySet() method
You can use the keySet() method of ConcurrentHashMap to get all keys from the map. This method returns a Set view of keys because keys are unique in Map. The returned Set is backed by the map, which means any change in the map will reflect in this Set, and any change in this Set will reflect in Map.For example, if you remove a key, then it will also get removed from the original Map. Here is an example of using key Set () with ConcurrentHashMap in Java:
If you print this Set, you will see all the keys from the ConcurrentHashMap like:
This way, you can pass a Set view of the Map to any method need Set. Btw, this Set is different from a normal set because you cannot add new keys on it, obviously adding keys without values doesn't make sense; hence compiler will throw UnsupportedOperationException if you try to add an element into the key set.
Set<String> books = bookAndPrice.keySet();
If you print this Set, you will see all the keys from the ConcurrentHashMap like:
System.out.println(bookAndPrice.keySet());
Output
[Effective Java]
This way, you can pass a Set view of the Map to any method need Set. Btw, this Set is different from a normal set because you cannot add new keys on it, obviously adding keys without values doesn't make sense; hence compiler will throw UnsupportedOperationException if you try to add an element into the key set.
8. How to get all values from ConcurrenntHashMap - values() example
Similar to the above example, you can also use the values() method of the Map interface to retrieve all values from the ConcurrentHashMap object. This method returns a Collection because Map allows duplicate values.The Collection is also backed by the original map, which means any change in value will reflect in the original map as shown in the following example:
If you have noticed, when you remove value from the Collection, the corresponding key from the ConcurrentHashMap has also been removed. So, be careful with that. See The Complete Java MasterClass to learn more.
Set<Map.Entry<String, Integer>> entries = bookAndPrice.entrySet();
entries.forEach(System.out::println); // print - Effective Java=42
If you look closely, you will find that each mapping is represented by a Map.Entry object, which is an excellent example of a nested static class. These objects keep the key and value together. It also provides methods like getkey() and getValue() to retrieve keys or values from the corresponding entry.
10. How to sort ConcurrentHashMap on keys in Java 8 (see here)
11. How to sort ConcurrentHashMap on values in Java 8 (see here)
12. How to update a value forgive key in ConcurrentHashMap (see here)
The remove(Object key) method removes the key (and its corresponding value) from this map. This method does nothing if the key is not on the map. It returns the existing value if a key is present or null otherwise. It also throws NullPointerException if the specified key is null. Here is an example to remove a key from ConcurrentHashMap in Java:
This will remove the mapping associated with the key "Head First Java," and the size of the map will be reduced by 1. There is another remove(Object key, Object value) method, which only removes a mapping if both key and value match to the specified key and value. It is equivalent to the following code except that it performs atomically:
Here is an example of removing a key-value pair from the Concurrent HashMap class in Java:
If you provide a different value, then the mapping will not be removed. You can check that by changing the value from 29 to 30 or something else.
From the output, you can see that all books which have "Java" in their title have been removed from the concurrent hash map.
ConcurrentMap<String, Integer> bookAndPrice = new ConcurrentHashMap<>();
bookAndPrice.put("Effective Java", 42);
System.out.println("before : " + bookAndPrice);
Collection<Integer> prices = bookAndPrice.values();
prices.remove(42);
System.out.println("after : " + bookAndPrice);
Output:
before : {Effective Java=42}
If you have noticed, when you remove value from the Collection, the corresponding key from the ConcurrentHashMap has also been removed. So, be careful with that. See The Complete Java MasterClass to learn more.
9. How to get all mappings from ConcurrentHashMap - entrySet() example
This is another method that is similar to the key Set () and values(). This method also returns a Set of all entries from the Map. It returns a set because each entry is unique. The entry set is also backed by the original map, and any change in the entry set will reflect on the map. Btw, this is the most efficient way to get all key and value pair, if you need both of them becuase just getting key and then looking up for value will take time.Set<Map.Entry<String, Integer>> entries = bookAndPrice.entrySet();
entries.forEach(System.out::println); // print - Effective Java=42
If you look closely, you will find that each mapping is represented by a Map.Entry object, which is an excellent example of a nested static class. These objects keep the key and value together. It also provides methods like getkey() and getValue() to retrieve keys or values from the corresponding entry.
10. How to sort ConcurrentHashMap on keys in Java 8 (see here)
11. How to sort ConcurrentHashMap on values in Java 8 (see here)
12. How to update a value forgive key in ConcurrentHashMap (see here)
13. How to remove the mapping from ConcurrentHashMap in Java
The ConcurrentHashMap class provides two remove() methods to remove a key or remove a key with a value. You can use any of these two methods to remove the mapping from the concurrent hash map. Remember, when you remove a key, the corresponding value is automatically removed from the map.The remove(Object key) method removes the key (and its corresponding value) from this map. This method does nothing if the key is not on the map. It returns the existing value if a key is present or null otherwise. It also throws NullPointerException if the specified key is null. Here is an example to remove a key from ConcurrentHashMap in Java:
bookAndPrice.remove("Head First Java");
This will remove the mapping associated with the key "Head First Java," and the size of the map will be reduced by 1. There is another remove(Object key, Object value) method, which only removes a mapping if both key and value match to the specified key and value. It is equivalent to the following code except that it performs atomically:
if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
map.remove(key);
return true;
} else
return false;
}
Here is an example of removing a key-value pair from the Concurrent HashMap class in Java:
bookAndPrice.remove("Head First Java", 29);
If you provide a different value, then the mapping will not be removed. You can check that by changing the value from 29 to 30 or something else.
14. How to remove a key/value pair while iterating over keys?
You can use Iterator's remove() method to remove a key-value pair while iterative over ConcurrentHashMap. This is no different than how you remove key/value pair from HashMap while iterating, but just remember that the Iterator returned by ConcurrentHashMap is fail-safe and will not throw ConcurrentModfiicationException if some other thread modifies the map while you are iterating over it. Here is the code to remove a mapping while iterating over ConcurrentHashMap in Java.ConcurrentHashMap<String, Integer> catelog = new ConcurrentHashMap<>();
catelog.put("Effective Java", 42);
catelog.put("Head First Java", 29);
catelog.put("Java Concurrency in Practice", 33);
catelog.put("Head First Design Patterns", 41);
System.out.println("before removing : " + catelog);
Iterator<String> iterator = catelog.keySet().iterator();
while(iterator.hasNext()){
if(iterator.next().contains("Java")){
iterator.remove();
}
}
System.out.println("after removing : " + catelog);
Output:
before removing : {Java Concurrency in Practice=33,
Head First Design Patterns=41, Effective Java=42, Head First Java=29}
after removing : {Head First Design Patterns=41}
From the output, you can see that all books which have "Java" in their title have been removed from the concurrent hash map.
15. putIfAbsent() example of Java 8
The putIfAbsent() method allows you to insert a mapping into ConcurrentHashMap only if the key provided is not present in the Map. You can use this for a check-and-insert kind of operation. If the key is present, then this method returns the existing value or null.The method will throw NullPointerException if either key or value is null because ConcurrentHashMap doesn't allow null keys or values. The null is often returned to denote the absence of value. Here is an example of the putIfAbsent() method to atomically update a counter:
ConcurrentHashMap<String, AtomicLong> map = new ConcurrentHashMap<>();
map.putIfAbsent("C#", new AtomicLong());
map.get("C#").incrementAndGet();
This code inserts the "C#" key if not present already with an AtomicLong counter. It then retrieves the value and increments the counter. The putIfAbsent() call ensures that mapping is always present before code tries to increment the counter. See The Complete Java MasterClass for more examples.
16. compute() example of Java 8
The JDK 8 also comes with a compute() method which allows you to perform an atomic operation by using key and values. For example, you can use the compute() method to atomically update an integer counter like a word counter using a lambda expression. Here is an example of how to do it in Java 8:ConcurrentMap<String, Long> wordCounter = new ConcurrentHashMap<>();
populationByCities.compute("Python",(key, value) -> value == null ? 1 : value + 1);
This code atomically updates a value corresponding to the key "Python". You have access to both key and value objects, and you are checking if the value is null, then initialize it with 1, or else, just add one into the existing value.
17. merge() example of Java 8
In JDK 8, you can use the merge method to atomically update a value. For example, if you have a key that keeps track of a counter, then you can update it atomically using the merge() method. It also allows you to initialize the counter without throwing a null pointer exception if the counter is not present as shown in the following example:ConcurrentMap<String, Long> populationByCities = new ConcurrentHashMap<>();
populationByCities.merge("Houston", 1L, (current, next) -> current + next);
You can further clean this code by using method reference and reduce methods added into various wrapper classes like Long and Integer.
populationByCities.merge("Chicago", 1L, Long::sum);
The above code will put "1" corresponding to the "Chicago" key if not present already; otherwise, it will just add one into the existing value by using the Long.sum() method. Though, unlike the compute() method, you only have access to value objects and not the key object.
18. mappingCount() example of Java 8
This is another new method added in JDK8 on ConcurrentHashMap class, which should be used in place of size() because a ConcurrentHashMap may contain more mappings than can be represented as an int. The problem of overflow is solved by adding the mappingCount() method, which returns a long value.long size = chm.mappingCount();
The value returned by mappingCount() is an estimate; the actual count may differ if there are concurrent insertions or removals.
19. The getOrDefault() example of Java 8
This is a new method added on JDK 8, which can be used to get a default value if a given key is not present in the ConcurrentHashMap. This can potentially prevent null pointer exceptions arise due to autoboxing null value returned by get() method if a key is not present. It can also be used to provide a reasonable default value. For exampleConcurrentHashMap<String, Integer> wordCount = new ConcurrentHashMap<>();
int count = wordCount.get("Java"); // throws NPE because get() return null
int count = wordCount.getOrDefault("Java", 0);
The getOrDefault() method returns the value to which the specified key is mapped, or the given default value if this map contains no mapping for the key.
20. How to check if a key exists in ConcurrentHashmap - containsKey() example
You can use the containsKey() method to check if a given is present in the ConcurrentHashMap or not. This method returns true if the given key exists otherwise it returns false. Here is the code example to use this method:if(bookAndPrice.containsKey("Effective Java")){
System.out.println("Effective Java is available");
}
Output:
Effective Java is available
In our case, the containsKey() returns true for key "Effective Java" because this book exists in our map of books and their prices.
21. How to check if a value exists in ConcurrentHashMap - containsValue() example
Similar to the previous example, you can also use the containsValue() method to check if a given value is present in the ConcurrentHashMap or not. This method returns true if a given value exists it the map, or false otherwise.Here is a code example to demonstrate how to use this method.
You can see that the isEmpty() method returned true because the copy map was empty and that's why the above line is printed, which also prints the size of ConcurrentHashMap, which is zero.
if(bookAndPrice.containsValue(42)){
System.out.println("Yes, book with price 42 is avaiable");
}
Output
Yes, book with price 42 is available
22. How to check if a ConcurrentHashMap is empty
You can use the isEmpty() method to check if a given ConcurrentHashMap is empty or not. A Map is said to empty; it contains no mapping. You can also use the size() method, which will return zero if there is no mapping, but isEmpty() is my preferred method. It is more readable than size. Here is a code example to check if given ConcurrentHashMap is empty or not:ConcurrentHashMap<String, Integer> copy = new ConcurrentHashMap<>();
if(copy.isEmpty()){
System.out.println("size of new ConcurrentHashMap: " + copy.size());
}
Output:
size of new ConcurrentHashMap: 0
You can see that the isEmpty() method returned true because the copy map was empty and that's why the above line is printed, which also prints the size of ConcurrentHashMap, which is zero.
23. How to copy all mappings from one ConcurrentHashMap to other
You can use the putAll() method of Map interface to copy all mappings from one ConcurrentHashMap to other. These mappings replace any mappings that this map had for any of the keys currently in the specified map.Here is the code example to copy entries from one Map to another in Java:
You can see that after copying the size of the new ConcurrentHashMap is also 4, as the original ConcurrentHashMap. You can even print the Map to see all mappings added to it.
ConcurrentHashMap<String, Integer> copy = new ConcurrentHashMap<>();
System.out.println("size beore copying: " + copy.size());
copy.putAll(bookAndPrice);
System.out.println("size after copying: " + copy.size());
Output:
size before copying: 0
size after copying: 4
You can see that after copying the size of the new ConcurrentHashMap is also 4, as the original ConcurrentHashMap. You can even print the Map to see all mappings added to it.
24. How to create ConcurrentHashSet from ConcurrentHashMap in Java 8
You can use the newly added newKeySet() method of ConcurrentHashMap to create a set backed by ConcurrentHashMap where values are Boolean.TRUE. There is no real ConcurrentHashset class, but the Set returned by newKeySet() behaves like a ConcurrentHashSet.The newKeySet() method is overloaded and it accepts an int as initial size as well.
You can pass this Set to any method which is accepting the Set interface.
ConcurrentHashMap<String, Integer> bookAndPrice = new ConcurrentHashMap<>();
Set<String> concurerntHashSet = bookAndPrice.newKeySet(bookAndPrice.size());
You can pass this Set to any method which is accepting the Set interface.
That's all about how to use ConcurrentHashMap in Java. In this article, you have learned how to perform basic operations like adding a key-value pair, retrieving values, retrieving all keys, all values, and all mappings, replacing an existing value, updating an existing value atomically, iterating over ConcurrentHashMap, removing a mapping or value during iteration, using newly added Java 8 methods like compute() and merge() for atomically updating values in ConcurrentHashMap and many more such examples.
If you think anything is left and you want to do something with concurrent HashMap which is not discussed here, then please drop a comment.
Other Java 8 tutorials you may like:
Thanks for reading this article, if you like this ConcurrentHashMap tutorial and examples then please share them with your friends and colleagues. If you have any issue, feedback or you are curious about anything related to ConcurrentHashMap then please drop a note.
P. S. - If you are looking for some free courses to learn recent changes on Java 8 and Java 9 then you can also see this list of Free Java 8 and 9 Courses for Programmers.
If you think anything is left and you want to do something with concurrent HashMap which is not discussed here, then please drop a comment.
Other Java 8 tutorials you may like:
- The Complete Java Developer RoadMap
- 5 Books to Learn Java 8 from Scratch
- 5 Courses to learn Java 8 in-depth
- How to join String in Java 8
- How to sort HashMap by values in Java 8?
- 10 examples of Optional in Java 8?
- How to sort the map by keys in Java 8?
- How to use the filter method in Java 8
- 10 Java 8 Stream Interview Questions with Answers
- How to use the forEach method in Java 8
Thanks for reading this article, if you like this ConcurrentHashMap tutorial and examples then please share them with your friends and colleagues. If you have any issue, feedback or you are curious about anything related to ConcurrentHashMap then please drop a note.
P. S. - If you are looking for some free courses to learn recent changes on Java 8 and Java 9 then you can also see this list of Free Java 8 and 9 Courses for Programmers.
bookmarked it, great how to guide with example
ReplyDelete