An example to convert a List<?> to a Map<K,V> using Java 8 Stream.
Java 8 – Collectors.toMap()
Let’s define a Pojo class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Person { private String email; private String name; private int age; public Person(String email, String name, int age) { this.email = email; this.name = name; this.age = age; } //Getters, setters @Override public String toString() { return "Person [email=" + email + ", name=" + name + ", age=" + age + "]"; } } |
In the first example, we convert a List<Person> in a Map<String, Person> that has email as key and the object itself as value.
1 2 3 4 5 6 7 8 9 10 11 |
List<Person> people = Arrays.asList( new Person("mario@reversecoding.net", "Mario", 27), new Person("luigi@reversecoding.net", "Luigi", 30), new Person("steve@reversecoding.net", "Steve", 20) ); Map<String, Person> mapEmailPerson = people.stream() .collect(Collectors.toMap(Person::getEmail, Function.identity())); System.out.println(mapEmailPerson); |
The output will be:
1 2 3 |
{steve@reversecoding.net=Person [email=steve@reversecoding.net, name=Steve, age=20], mario@reversecoding.net=Person [email=mario@reversecoding.net, name=Mario, age=27], luigi@reversecoding.net=Person [email=luigi@reversecoding.net, name=Luigi, age=30]} |
Or using lambda:
1 2 3 4 5 |
Map<String, Person> mapEmailPerson = people.stream() .collect(Collectors.toMap(person -> person.getEmail(), person -> person)); System.out.println(mapEmailPerson); |
Output:
1 2 3 |
{steve@reversecoding.net=Person [email=steve@reversecoding.net, name=Steve, age=20], mario@reversecoding.net=Person [email=mario@reversecoding.net, name=Mario, age=27], luigi@reversecoding.net=Person [email=luigi@reversecoding.net, name=Luigi, age=30]} |
Let’s break this out
First of all, we create a Stream of Person from the List<Person> defined.
Then we collect this stream in a Map. Java 8 helps us to define the needed Collector by providing us the method: Collectors.toMap().
Collectors.toMap() takes two functions – one for mapping the key and one for the value – and returns a Collector
that accumulates elements into a Map.
Since we are working with Stream of Person – our input it’s an object Person.
We have chosen the email as key, so that is a function that given the input – Person – returns its email:
1 2 3 4 5 |
//Given a person, we get his email person -> person.getEmail() //Or using method reference Person::getEmail() |
and then the object itself as value, so it’s just an identity function:
1 2 3 4 5 |
//Given a person, we want as value the person itself person -> person //Or Function.identity() |
These are the parameters for toMap.
Another example
Given a List<Person> we want to create a Map<String, Integer> that contains the name as key and the age as value.
We just need to change the two parameters of the Collectors.toMap, by specifying:
1 2 3 4 5 |
//Name as key person -> person.getName() //Age as value person -> person.getAge() |
So the code will be:
1 2 3 4 5 6 7 8 9 10 11 |
List<Person> people = Arrays.asList( new Person("mario@reversecoding.net", "Mario", 27), new Person("luigi@reversecoding.net", "Luigi", 30), new Person("steve@reversecoding.net", "Steve", 20) ); Map<String, Integer> mapNameAge = people.stream() .collect(Collectors.toMap(person -> person.getName(), person -> person.getAge())); System.out.println(mapNameAge); |
Output:
1 |
{Steve=20, Luigi=30, Mario=27} |
If you are a good observer you may have noticed that the order hasn’t been respected. That’s because the default implementation used by toMap is the HashMap that does not guarantee the order of the map.
Java 8 – Collectors.toMap with a LinkedHashMap
If we want to preserve the order we should use a LinkedHashMap instead of the HashMap.
Let’s try the previous example by passing a LinkedHashMap to Collectors.toMap()
1 2 3 4 5 6 7 8 9 10 |
Map<String, Integer> mapNameAge = people.stream() .collect(Collectors.toMap( Person::getName, Person::getAge, (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new )); System.out.println(mapNameAge); |
Output:
1 |
{Mario=27, Luigi=30, Steve=20} |
We are using the definition of toMap that takes four parameters:
keyMapper
– a mapping function to produce keysvalueMapper
– a mapping function to produce valuesmergeFunction
– a merge function used to resolve collisions between values associated with the same keymapSupplier
– a function which returns a new, emptyMap
into which the results will be inserted
We’ve already discussed the first two parameters.
In case of a collision we just want to throw an exception, so as third parameter we define that. In the example, we used the same implementation of the static method throwingMerger defined in the java.util.stream.Collectors class.
The fourth parameter it’s the one in which we define a function that returns our LinkedHashMap.