1. Overview
In this tutorial, We'll learn the usage of toMap() method of the Collectors class that is added in the jdk 8.
toMap() method is a static method from Collectors class from java 8 onwards.
Collectors.toMap() method is used to convert the stream of object into the Map as needed with the help 3 overloaded methods.
2. Collectos.toMap() Syntax
toMap() method syntax from java 8 api.
public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction) public static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
3. Collectors toMap() To Convert List to Map
If we have list of objects such as List of strings and this list has to be converted into the map. In the Map, key is the string and value is its length.
Look at the below code how Collectos.toMap() method can be used to transform List to Map.
package com.javaprogramto.java8.collectors.tomap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; public class CollectorsToMapExample { public static void main(String[] args) { List<String> numbersinStringList = new ArrayList<>(); numbersinStringList.add("One"); numbersinStringList.add("Two"); numbersinStringList.add("Three"); numbersinStringList.add("Four"); numbersinStringList.add("Five"); Map<String, Integer> map = numbersinStringList.stream() .collect(Collectors.toMap(Function.identity(), String::length)); System.out.println("List : "+numbersinStringList); System.out.println("Map : "+map); } }
Output:
List : [One, Two, Three, Four, Five] Map : {Five=4, One=3, Four=4, Two=3, Three=5}
4. Collectors toMap To Convert List to Map Using Custom Objects
In the previous section, example is based on the List of Strings. But in the realtime applications, we mostly working with the collection of obejcts. Those objects can be any type of user defined types.
So, to handle custom objects with toMap() method is little tricky.
Let us look at the below program that we are going to add the Employee objects to List first and next converting into a map with key as emp id and value is full emp object.
Employee class
package com.javaprogramto.java8.compare; import java.util.Objects; public class Employee { private int id; private String name; private int age; public Employee(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return id == employee.id && age == employee.age && Objects.equals(name, employee.name); } @Override public int hashCode() { return Objects.hash(id, name, age); } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + "} \n"; } }
toMap() method with Custom objects:
package com.javaprogramto.java8.collectors.tomap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import com.javaprogramto.java8.compare.Employee; public class CollectorsToMapExample2 { public static void main(String[] args) { List<Employee> employeeList = new ArrayList<>(); employeeList.add(new Employee(1, "One", 20)); employeeList.add(new Employee(2, "Two", 30)); employeeList.add(new Employee(3, "Three", 40)); employeeList.add(new Employee(4, "Four", 25)); employeeList.add(new Employee(5, "Give", 35)); Map<Integer, Employee> map = employeeList.stream() .collect(Collectors.toMap(Employee::getId, Function.identity())); System.out.println("List : "+employeeList); System.out.println("Map : "+map); } }
Output:
List : [Employee{id=1, name='One', age=20} , Employee{id=2, name='Two', age=30} , Employee{id=3, name='Three', age=40} , Employee{id=4, name='Four', age=25} , Employee{id=5, name='Give', age=35} ] Map : {1=Employee{id=1, name='One', age=20} , 2=Employee{id=2, name='Two', age=30} , 3=Employee{id=3, name='Three', age=40} , 4=Employee{id=4, name='Four', age=25} , 5=Employee{id=5, name='Give', age=35} }
This program compiles and executes without any errors.
You can see the map is with emp id as key and emp object as value.
5. Collectors toMap To Convert List to Map Using Custom duplicate keys
The above section program works flawlessly when all emp id's are unique. What happens if the list has the same employee instance added twice or more.
Just add the below code to the program and run it.
employeeList.add(new Employee(5, "Give", 35)); employeeList.add(new Employee(5, "Give", 35));
Executin is failed with the error "Exception in thread "main" java.lang.IllegalStateException: Duplicate key 5"
It is clearly saying deplicate key with value 5.
How to handle these type of cases when using stream collectors tomap() method.
When the same key is added to the HashMap, it does not show up any error. It replaces the exisitng value with the new one for the duplicate key.
To achive these behaviour, we need to use toMap() overloaded method with thrid argument BinaryOperator.
Look at the beloe code.
package com.javaprogramto.java8.collectors.tomap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import com.javaprogramto.java8.compare.Employee; public class CollectorsToMapExample3 { public static void main(String[] args) { List<Employee> employeeList = new ArrayList<>(); employeeList.add(new Employee(1, "One", 20)); employeeList.add(new Employee(2, "Two", 30)); employeeList.add(new Employee(3, "Three", 40)); employeeList.add(new Employee(4, "Four", 25)); employeeList.add(new Employee(5, "Give", 35)); employeeList.add(new Employee(5, "Giver", 36)); Map<Integer, Employee> map = employeeList.stream() .collect(Collectors.toMap(Employee::getId, Function.identity(), (oldVal, newVal) -> newVal)); System.out.println("List : " + employeeList); System.out.println("Map : " + map); } }
Output:
List : [Employee{id=1, name='One', age=20} , Employee{id=2, name='Two', age=30} , Employee{id=3, name='Three', age=40} , Employee{id=4, name='Four', age=25} , Employee{id=5, name='Give', age=35} , Employee{id=5, name='Giver', age=36} ] Map : {1=Employee{id=1, name='One', age=20} , 2=Employee{id=2, name='Two', age=30} , 3=Employee{id=3, name='Three', age=40} , 4=Employee{id=4, name='Four', age=25} , 5=Employee{id=5, name='Giver', age=36} }
6. Collectors toMap To Other Map Types
As of now we wrote the examples. In all of these returned map is HashMap. That means toMap() method returns HashMap by default.
If we want to convert into the different Map implmentation then we need to use another overloaded method of toMap() with 4th argument Supplier.
Look at the below examples to convert the list into TreeMap and ConcurrentHashMap.
TreeMap is a sorted map by key where as ConcurrentHashMap works in multithreaded enviornments.
package com.javaprogramto.java8.collectors.tomap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import com.javaprogramto.java8.compare.Employee; public class CollectorsToMapExample4 { public static void main(String[] args) { List<Employee> employeeList = new ArrayList<>(); employeeList.add(new Employee(1, "One", 20)); employeeList.add(new Employee(2, "Two", 30)); employeeList.add(new Employee(3, "Three", 40)); employeeList.add(new Employee(4, "Four", 25)); employeeList.add(new Employee(5, "Give", 35)); employeeList.add(new Employee(5, "Giver", 36)); // TreeMap example // converting duplicate keys into the TreeMap Map<Integer, Employee> treeMap = employeeList.stream().collect( Collectors.toMap(Employee::getId, Function.identity(), (oldVal, newVal) -> newVal, TreeMap::new)); System.out.println("ArrayList : " + employeeList); System.out.println("TreeMap : " + treeMap); // ConcurrentHashMap example // converting duplicate keys into the ConcurrentHashMap Map<Integer, Employee> concurrentHashMap = employeeList.stream().collect(Collectors.toMap(Employee::getId, Function.identity(), (oldVal, newVal) -> newVal, ConcurrentHashMap::new)); System.out.println("ArrayList : " + employeeList); System.out.println("ConcurrentHashMap : " + concurrentHashMap); } }
7. Conclusion
In this article, We've seen how to use Collectors.toMap() method and how to handle the duplicate keys in Map when the source list is with duplicates.
How to convert the stream into TreeMap or any other Map types.
No comments:
Post a Comment
Please do not add any spam links in the comments section.