Java 8 - Map's computeIfAbsent, computeIfPresent, getOrDefault methods tutorial with examples
This is the last article, of a 4 part article series, covering the significant enhancements in Java 8 Collections API. While the 1st part of the series explained the new default methods introduced in
In this tutorial, we will be looking at the enhancements introduced in
A good example of a use-case for a multi-value map would be of the hash table used to store hash keys and corresponding values. In a hash table, there exists a mappping between a hash value(key) and entries stored in a bucket (value) stored corresponding to it. A hash table is thus essentially a multi-value map.
Having understood the structure of a multi-value map, let us now define the base data set for showing the working of the new
Explanation of the code
Where,
-
- method applies the logic provided via
Where,
-
- the method applies the logic provided via
Let us now see the
OUTPUT of the above code
Explanation of the code
Java 8's
Where,
-
-
Have a look at the code sample below to understand the difference in code when using Java 7 versus Java 8's
OUTPUT of the above code
Explanation of the code
Where,
-
-
To understand the usage of
OUTPUT of the above code
Explanation of the code
Where,
-
-
To understand the working of the
OUTPUT of the above code
Explanation of the code
Iterable
and Iterator
interfaces in Java 8(read 1st partRead Part 1-Iterable.forEach,Iterator.remove methods tutorial), the 2nd part covered the new default method removeIf()
introduced in Collection
interface, (read 2nd partRead Part 2- Collection.removeIf method tutorial), and the 3rd explained List
interface's new sort()
and replaceAll()
methods(read 3rd partRead Part 3-List.sort & List.replaceAll methods tutorial).In this tutorial, we will be looking at the enhancements introduced in
java.util.Map
interface in Java 8. We will first quickly understand what are multi-value maps. Next we will create a multi-value map which will serve as the base problem set for explaining the new Map methods. We will first see the working of Java 8's new Map.forEach()
and Map.replaceAll()
methods. Next, we will understand the new default methods introduced in Java 8 which simplify using multi-value maps. These methods are Map.computeIfAbsent()
, Map.computeIfPresent()
and Map.getOrDefault()
methods.
What is a multi-value map
A multi-value map is a normal instance of java.util.Map
. The only difference is that instead of having a 'single' value corresponding to each key, a multi-value map instead has a 'collection' such as a List
or a Set
as the value stored against each key.
Map
methods -
Defining the data set/multi-value Map for this tutorial
Java 8 code defining a multi-value map
package com.javabrahman.java8;
public class Employee {
private String name;
private Integer age;
private Double salary;
public Employee(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String toString(){
DecimalFormat dformat = new DecimalFormat(".##");
return "Employee Name:"+this.name
+" Age:"+this.age
+" Salary:"+dformat.format(this.salary);
}
//getters and setters for name, age and salary go here
//standard equals() and hashcode() code go here
}
//MultiValueMapsExample.java
package com.javabrahman.java8.collections;
import com.javabrahman.java8.Employee;
import java.util.*;
public class MultiValueMapsExample {
static Map<Integer, List<Employee>> employeeDOJMap = new HashMap<>();
public static void main(String args[]) {
List<Employee> list2014 = Arrays.asList(
new Employee("Deborah Sprightly", 29, 9000.00));
employeeDOJMap.put(2014, list2014);
List<Employee> list2015 = Arrays.asList(
new Employee("Tom Jones", 45, 7000.00),
new Employee("Harry Major", 25, 10000.00));
employeeDOJMap.put(2015, list2015);
List<Employee> list2016 = Arrays.asList(
new Employee("Ethan Hardy", 65, 8000.00),
new Employee("Nancy Smith", 22, 12000.00));
employeeDOJMap.put(2016, list2016);
}
}
Employee
class is the POJO for employees. It contains three attributes of an employee -name
,age
andsalary
.employeeDOJMap
is the multi-value map of typeMap<Integer, List<Employee>>
.employeeDOJMap
’sInteger
key contains the year of joining of employees as the key, while theList<Employee>>
stored as value for each key contains the instances ofEmployee
objects who joined in that year.
Map.forEach()
and Map.replaceAll()
methods
Let us start by quickly going through the relatively simpler methods introduced in Map interface in Java 8- Map.forEach()
and Map.replaceAll()
.
Map.forEach()
Map.forEach()
method is defined as -
default void forEach(BiConsumer<? super K, ? super V> action)
-
action
is the only parameter and is an instance of a BiConsumer functional interface, and, - method applies the logic provided via
action
to all the entries in the map as it 'consumes' them.
What is a BiConsumer
java.util.function.BiConsumer
is a functional interface, and is a two-arity specialization of a Consumer Functional InterfaceClick to read detailed tutorial on Consumer Functional Interfaces. I.e. it accepts two inputs as arguments and does not return any output.
Map.replaceAll()
Map.replaceAll()
method is defined as -
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
-
function
is the only parameter and is an instance of a BiFunction
Functional Interface, and, - the method applies the logic provided via
function
to all the values in the map and transforms them inside the map itself.
What is a BiFunction
java.util.function.BiFunction
is a functional interface, and is a two-arity specialization of FunctionClick to read detailed tutorial on Java 8's Function Interface. I.e. it accepts two inputs as arguments and returns a result after performing a computation with the input.
Map.forEach()
and Map.replaceAll()
methods in action - Java 8 code showing Map.forEach() and Map.replaceAll() methods
System.out.println("Using Map.forEach to print the Employee in employeeDOJMap multi-value map\n");
employeeDOJMap.forEach((year,empList)->System.out.println(year+"-->" +empList));
System.out.println("\nCAPITALIZED Employee Names using Map.replaceAll()");
employeeDOJMap.replaceAll((year, empList) -> {
empList.replaceAll(emp -> {
emp.setName(emp.getName().toUpperCase());
return emp;
});
return empList;
});
employeeDOJMap.forEach((year, empList)-> System.out.println(year+"-->"+empList));
Using Map.forEach to print the Employee in employeeDOJMap multi-value map -
2016-->[Employee Name: Ethan Hardy Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2014-->[Employee Name: Deborah Sprightly Age: 29 Salary: 9000.0] 2015-->[Employee Name: Tom Jones Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
CAPITALIZED Employee Names using Map.replaceAll()
2016-->[Employee Name: ETHAN HARDY Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2014-->[Employee Name: DEBORAH SPRIGHTLY Age: 29 Salary: 9000.0] 2015-->[Employee Name: TOM JONES Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
Map.forEach()
method is passed the lambda expressionRead Java 8 Lambda Expressions Tutorial equivalent to aBiConsumer
implementation which prints the keys(years) and the corresponding values(employee lists) stored in theemployeeDOJMap
.Map.replaceAll()
method uses a lambda equivalent of aBiFunction
implementation. This lambda uses aList.replaceAll()
method to replace all the employee names stored inside each of theList<Employee>
, with their capitalized versions.
Map.computeIfAbsent()
method
As we learnt earlier, a multi-value map stores a collection of values for each key. Lets say we are adding a [key,value] entry to a multi-value map and the key we are adding is not present in the map. This would require for us to check for this probability before insertion and if the key is not present then we will have to instantiate a fresh collection instance as the value for this new key. Only then can we store the value against the key. Also, this check will need to be performed on every insert that we do.Map.computeIfAbsent()
takes away exactly this overhead of writing the multiple line code for such a check by squeezing it into a simple one line code. Map.computeIfAbsent()
method is defined as - default V computeIfAbsent(K key,Function<? super K,? extends V>mappingFunction)
-
key
is the first parameter which is the key of the multi-value map. -
function
is an instance of java.util.function.Function
. It computes and returns the value which is to be used when the key is new i.e. does not have a collection instantiated in the case of a multi-value map.Have a look at the code sample below to understand the difference in code when using Java 7 versus Java 8's
computeIfAbsent()
method. The red code is Java 7 way of checking and instantiating before all insertions. The green code is Java 8 way of doing the same thing using Map.computeIfAbsent()
method.Java 8 code to show usage of Map.computeIfAbsent() method
System.out.println("\nJava 7 way of adding a new key(2017) in a multi-value map\n");[/java]
List empList2017 = employeeDOJMap.get(2017);
if (empList2017 == null) {
empList2017 = new ArrayList<>();
}
empList2017.add(new Employee("Tom Newman", 45, 12000.00));
employeeDOJMap.put(2017, empList2017);
[java]employeeDOJMap.forEach((year,empList)-> System.out.println(year+"-->"+empList));
System.out.println("\nUsing Map.computeIfAbsent() to add a new key(2018) in a multi-value map\n");[/java]employeeDOJMap.computeIfAbsent(2018,empList -> new ArrayList<>())
.add(new Employee("Dick Newman", 35, 10000.00));
[java]employeeDOJMap.forEach((year,empList)-> System.out.println(year+"-->"+empList));
Java 7 way of adding a new key(2017) in a multi-value map
2016-->[Employee Name: Ethan Hardy Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2017-->[Employee Name: Tom Newman Age: 45 Salary: 12000.0] 2014-->[Employee Name: Deborah Sprightly Age: 29 Salary: 9000.0] 2015-->[Employee Name: Tom Jones Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
Using Map.computeIfAbsent() to add a new key(2018) in a multi-value map
2016-->[Employee Name: Ethan Hardy Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2017-->[Employee Name: Tom Newman Age: 45 Salary: 12000.0] 2018-->[Employee Name: Dick Newman Age: 35 Salary: 10000.0] 2014-->[Employee Name: Deborah Sprightly Age: 29 Salary: 9000.0] 2015-->[Employee Name: Tom Jones Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
- The red code shows the Java 7 way of adding an employee named
Tom Newman
for the key2017
. It is a 5-6 line code. - The green code shows Java 8's
computeIfAbsent()
method usage. A lambda expression implementing theBiFunction
logic is passed to the method which instantiates and anArrayList
instance if it is empty(i.e. absent). To thisArrayList
instance is added the new employee namedDick Newman
. It is a one line code. - The output shows the
employeeDOJMap
printed usingmap.forEach()
method after each of the two additions. This is shown to see that there is no difference in the way the new entries(key-value pairs) in the multi-value map are stored when using the Java 7 way and the Java 8computeIfAbsent()
method.
Map.computeIfPresent()
method
When working with multi-value maps, there are scenarios when while deleting a [key,value] pair, we might be removing the last/only value in the collection stored as the value for that key. In such cases, after removing the value from the collection, we would want to release the memory occupied by that the empty collection by removing that key from the multi-value map itself. This would require us to check after 'every' removal whether the value being removed is the last value for that key, and if so, remove the key from the multi-value map.Map.computeIfPresent()
takes away exactly this overhead of checking after every removal by reducing it to a simple one-line code. Map.computeIfPresent()
method is defined as -
default V computeIfPresent(K key,BiFunction<? super K,? super V,? extends V>remappingFunction)
-
key
is the first parameter which is the key of the multi-value map. -
remappingFunction
is an instance of a BiFunction
. It computes and returns a value. In case of multi-value maps, the outcome of the key's collection is decided based the value returned by this function. I.e. whether to keep the collection(if the collection is returned) or delete the collection(if a null value is returned).To understand the usage of
computeIfPresent()
method better, let us have a look at the code sample below to understand the difference in code when using Java 7 versus Java 8's computeIfPresent()
method. As we did for the previous method, we will use color coding to differentiate between the Java 7 and Java 8 code. The red code is Java 7 way of checking and removing a key after every removal from the map. The green code is Java 8 way of doing the same thing using Map.computeIfPresent()
method.Java 8 code to show usage of Map.computeIfPresent() method
System.out.println("\nJava 7 way of removing a key(2017) in a multi-value map for which no entry exists\n");
List empListDel = employeeDOJMap.get(2017);
empListDel.removeIf(employee -> employee.getName().equals("Tom Newman"));
if (empListDel.size() == 0) {
employeeDOJMap.remove(2017);
}
employeeDOJMap.forEach((year, empList)-> System.out.println(year+"-->"+empList));
System.out.println("\nUsing Map.computeIfPresent() to remove a key(2018) for which no entry exists\n");
employeeDOJMap.computeIfPresent(2018, (year, empList) -> empList.removeIf(employee -> employee.getName().equals("Dick Newman")) && empList.size() == 0 ? null : empList);
employeeDOJMap.forEach((year, empList)-> System.out.println(year+"-->"+empList));
Java 7 way of removing a key(2017) in a multi-value map for which no entry exists
2016-->[Employee Name: Ethan Hardy Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2018-->[Employee Name: Dick Newman Age: 35 Salary: 10000.0] 2014-->[Employee Name: Deborah Sprightly Age: 29 Salary: 9000.0] 2015-->[Employee Name: Tom Jones Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
Using Map.computeIfPresent() to remove a key(2018) for which no entry exists
2016-->[Employee Name: Ethan Hardy Age: 65 Salary: 8000.0, Employee Name: Nancy Smith Age: 22 Salary: 12000.0] 2014-->[Employee Name: Deborah Sprightly Age: 29 Salary: 9000.0] 2015-->[Employee Name: Tom Jones Age: 45 Salary: 7000.0, Employee Name: Harry Major Age:25 Salary: 10000.0]
- The red code shows the Java 7 way of removing the newly added employee for
2017
namedTim Newman
.List.removeIf()
method is used for finding out the employee and removing him. It is a 4-5 line code. - The green code shows Java 8 way of removing the only employee stored in the
List
for year2018
-Dick Newman
. It uses thecomputeIfPresent()
method, to which a lambda expression equivalent to the BiFunction implementation is passed. This lambda checks if the list is empty after removing the aforesaid employee. If the list is empty the lambda returns anull
else it returned theempList
object itself. ThecomputeIfPresent()
method deletes the entry for the key 2018 if null is returned. This is a one line code, abeit a long line of code. - The output shows the
employeeDOJMap
printed usingmap.forEach()
method after each of the two additions. This is shown to see that there is no difference in the way entries(key-value pairs) are removed from the multi-value map when using the Java 7 way as compared to the Java 8computeIfAbsent()
way.
Map.getOrDefault()
method
Map.getOrDefault()
method has been designed for scenarios where the value returned for a given key might be null i.e. the given key is not present in the map. In case of multi-value maps, it gives the programmer a utility to avoid NullPointerException
at runtime by instantiating a new collection instance and returning it in case the key is not present and a null-value would otherwise have been returned.Map.getOrDefault()
is defined as follows -
default V getOrDefault(Object key, V defaultValue)
-
key
is the first parameter which is the key of the multi-value map. -
defaultValue
is the value which will be used as default in case the key is not present and a null is returned.To understand the working of the
Map.getOrDefault()
method better letter look at the code snippet showing its usage - Java 8 code to show usage of Map.getOrDefault() method
System.out.println("\nAvoiding a null return when fetching a non-existent key's entry using Map.getOrDefault() method\n");
List<Employee> empList2019 = employeeDOJMap.getOrDefault(2019, new ArrayList<>());
System.out.println("Size of empList 2019 = " + empList2019.size());
Avoiding a null return when fetching a non-existent key's entry using Map.getOrDefault() method Size of empList 2019 = 0
getOrDefault()
method is used to fetch the value for the key2019
. The default value is set as a new emptyArrayList()
.- Since the key
2019
does not exist, the emptyArrayList
is returned instead. The size of thisArrayList
is then printed. As expected the value of size is printed as0
.
Map
interface in Java 8. The new Map
methods we covered in detail are - forEach()
, replaceAll()
, computeIfAbsent()
, computeIfPresent()
and getOrDefault()
method. With this we conclude the 4-part series covering the Java 8 Collection enhancements.Continue Reading on Java 8 Collection Enhancements
Part 1 - Iterable.forEach, Iterator.remove methods tutorial with examplesRead Java 8 Collection Enhancements- Part 1Part 2 - Collection.removeIf method tutorial with examplesRead Java 8 Collection Enhancements- Part 2Part 3 - List.sort, List.replaceAll methods tutorial with examplesRead Java 8 Collection Enhancements- Part 3 Part 4 - Map's computeIfAbsent, computeIfPresent, getOrDefault methods tutorial with examplesRead Java 8 Collection Enhancements- Part 4
Part 1 - Iterable.forEach, Iterator.remove methods tutorial with examplesRead Java 8 Collection Enhancements- Part 1Part 2 - Collection.removeIf method tutorial with examplesRead Java 8 Collection Enhancements- Part 2Part 3 - List.sort, List.replaceAll methods tutorial with examplesRead Java 8 Collection Enhancements- Part 3 Part 4 - Map's computeIfAbsent, computeIfPresent, getOrDefault methods tutorial with examplesRead Java 8 Collection Enhancements- Part 4