Java 8 – Convert list to map

In this article, we will understand how to convert a list of objects to a map using toMap() method of Collectors class added in java 8.
field values of objects in this list will form the Key and value pairs of returned map.

Below is the class whose objects will be added to the list that will be converted to a map.

public class Laptop {
  private String brand;
  private String model;

  public Laptop(String b, String m) {
    this.brand = b;
    this.model = m;
  }

  // getters and setters
}

Following is the code snippet that creates objects of this class and adds them to a list

List<Laptop> list = new ArrayList<>();
Laptop l1 = new Laptop("Apple", "X");
Laptop l2 = new Laptop("Dell", "Y");
Laptop l3 = new Laptop("Sony", "Z");
list.add(l1);
list.add(l2);
list.add(l3);

We will be converting this list to a map using Collectors.toMap() method. Keys of this map will be brand field and value will be the model field of Laptop objects.

Collectors.toMap()
toMap() method is a static method added to Collectors class. It takes below 2 arguments.
Each argument is a function that should return a value. Value returned by the first argument forms the key of resultant map and second argument result becomes the value of corresponding key.

1. An argument of type java.util.function.Function.
This is a functional interface with a method apply(), which takes a single argument and returns a value.
As stated before, this value will be the key of each map entry.

2. An argument of type java.util.function.Function.
Value returned by this function will be the value of each map entry.

Both the arguments are Functional interfaces, so they can be represented by Lambda expressions.
Below is the usage example of toMap() based on above explanation

Map<String, String> map = list.stream().
                          collect(
                          Collectors.toMap(
                             l -> l.getBrand(), 
                             l -> l.getModel()));
System.out.println(map);

Here is the output map

{Sony=Z, Dell=Y, Apple=X}

Handling duplicate keys
If the list contains more than one objects with same value of a field that is used as a key in toMap(), it throws an error.
Thus, if below objects are added to a list which is converted to a map,

Laptop l1 = new Laptop("Apple", "X");
Laptop l2 = new Laptop("Dell", "Y");
Laptop l3 = new Laptop("Sony", "Z");
Laptop l4 = new Laptop("Sony", "Z1");

toMap() will throw an error

Exception in thread “main” java.lang.IllegalStateException: Duplicate key Sony (attempted merging values Z and Z1)

This is because when there are duplicate keys, toMap() does not know which value it should use for that key and it throws an error.

To resolve, we must configure toMap() so that it knows which value to use when the keys are duplicate. For this, we must use overloaded version of toMap() that takes an additional merge function as the third argument.

This merge function is of type java.util.function.BinaryOperator, which is a functional interface having apply() method.
It accepts two arguments and returns a value. The arguments that it receives are the existing(or old) value for the key and the new value.
Example of toMap() with merge function to resolve duplicate key conflict is

Map<String, String> map = list.stream().
                          collect(
                          Collectors.toMap(l -> l.getBrand(), 
                              l -> l.getModel(), 
                              (oldVal, newVal) -> newVal));

This means that later(or the last) value for the same key will be replaced with any existing value. If you wish to keep the first value, then return the first argument from merge function.

For above objects, the resultant map will be

{Sony=Z1, Dell=Y, Apple=X}

Creating other map types
By default, toMap() returns an instance of HashMap. We can choose to return a different map type using another overloaded version of toMap(), that takes a fourth argument of type java.util.function.Supplier interface.

It is also a functional interface with a method get() that takes no arguments and returns a value.
So, we can implement it as a Lambda expression and return an instance of the type of map that the list should be converted to. Example,

LinkedHashMap<String, String> map2 = list.stream().
                                     collect(
                                     Collectors.toMap(
                                        l -> l.getBrand(), l -> l.getModel(), 
                                        (oldVal, newVal) -> newVal, 
                                        () -> new LinkedHashMap()));

Fourth argument can also be written using method references as

LinkedHashMap<String, String> map = list.stream().
                                     collect(
                                     Collectors.toMap(
                                      l -> l.getBrand(), l -> l.getModel(), 
                                      (oldVal, newVal) -> newVal, 
                                      LinkedHashMap::new));

That is all on converting a list of objects to a map in java using Collectors.toMap().

Hope the article was useful.