Top 25 Examples of ConcurrentHashMap in Java 8 - Tutorial

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 which 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 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 large map where number of keys exceeds 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.




22 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 on 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 of the 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 becuase 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 into 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 return the value or null if no value is associated with the key. Here is an example of retreiving value from ConcurrentHashMap in Java:

Integer price = bookAndPrice.get("Effective Java");; //return 42;
Integer price2 = bookAndPrice.get("Head First Java"); // return null

The get() method uses hashCode() of the key object to find values. If you remember, its important for both key and value objects to implement equals() and hashcode() method to be stored inside a Map and ConcurrentHashMap is not an exception to that. Ultimately, it is still a Map.


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 i.e., Integer.MAX_VALUE. Instead, a new method mappingCount() has been added, which returns a long value. See The Complete Java MasterClass 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 replace the value if old value is the same as 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.

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 e.g. 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 e.g. 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:

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 becuase 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 ConcurerntHashMap - values() example

Similar to above example, you can also use the values() method of Map interface to retrieve all values from the ConcurrentHashMap object. This method return a Collection because Map allows duplicate values. The Collection is also backed by origional map, which means any change in value will reflect in the original map as shown in following example:

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}
after : {}

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 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 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 key or value 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 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 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

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 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 AtomiLong 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 a compute() method which allows you to perform atomic operation by using key and values. For example, you can use the compute() method to atomicall update an integer counter like word counter using 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 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 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 existing value by using 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 dud 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 example

ConcurrentHashMap<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 return true if give key exists other wise it return 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 he map, or false otherwise. Here is a code example to demonstrate how to use this method.

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 returend true becuase copy map was empty and that's why the above line is printed, which also prints the size of ConcurrentHasMap, 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 other in Java:

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 beore copying: 0
size after copying: 4

You can see that after copying the size of new ConcurrentHashMap is also 4, as the original ConcurrentHashMap. You can even print the Map to see all mappings added into 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.

ConcurrentHashMap<String, Integer> bookAndPrice = new ConcurrentHashMap<>();
Set<String> concurerntHashSet = bookAndPrice.newKeySet(bookAndPrice.size());

You can pass this Set to any method which is accepting Set interface.

That's all about how to use ConcurrentHashMap in Java. In this article, you have learnd how to peform basic operations like adding a key value pair, retrieving values, retreiving all keys, all values, and all mapings, 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 CocurrentHashMap 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.

Further Reading
What's New in Java 8
Java SE 8 for the Impatient
The Complete Java MasterClass


Other Java 8 tutorials you may like:


Thanks for reading this article, if you like this ConcurrentHashMap tutorial and exmaples then please share 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.

No comments:

Post a Comment