How to convert List into Map in Java 8 - Example Tutorial

One of the common tasks in Java programming is to convert a list to map and I have written about this in past and today we'll see how Java 8 makes this task easier. Btw, be it Java 7 or Java 8, you need to keep something in mind while converting a list to map because they are two different data structure. For example, the list allows duplicate elements but keys in a map must be unique, the value can be duplicated but a duplicate key may cause a problem in Java 8. Similarly, another concern is order. A list is an ordered collection but the map doesn't guarantee any order unless you decide to use LinkedHashMap, which keeps insertion order or TreeMap which keeps mapping in the sorted order of keys. This is one of the important details which many Java beginners forget and then spend hours to chase subtle bugs.

If your program has any dependency on the order of elements in the list they will not work as expected if you use map. So, you should be mindful of these general details while converting a list to map in Java. This is true irrespective of the Java version.

Anyway, let's see the task at hand. Assume you have a list of courses and you want to create a map where keys should be the title of the course and value should be the course object itself. How will you do that?


How to convert List<V> into Map<K, V> in Java 7

It's easy in Java 7, all you need to do is go through the List, extract a key from each object and add both key and value into the map, but how will you do that in Java 8 style e.g. by using lambda expression and streams?


We'll see that but let's first write the JDK 7 version to convert a List<V> into Map<K, V>:

private Map<String, Course> toMap(List<Course> listOfCourses) {
    final Map<String, Course> courses = new HashMap<>();
    for (final Course current : listOfCourses) {
      hashMap.put(current.getTitle(), current);
    }
    return courses;
  }

This code is very simple and easy to read, let's now see the Java 8 version to find out whether Java 8 really makes your life easy when it comes to writing day to day code:

Map<String, Course > result = listOfCourses
                                 .stream()
                                 .collect(
                                 Collectors.toMap(Course::getTitle,
                                                 Function.identity()));

Wow, it just took one line to convert a list of objects into a map, which had taken one function in JDK 7. So, it looks Java 8 really makes the developer's life easy.

Anyway, let's try to understand what's going on here. Well, you have a listOfCourses, which is a List and then you called the stream() method which returns a Stream associated with that list. After that, you have called the collect() method which is used to accumulate elements from Stream.

Since we are not doing any filtering there is no call to filter(), the collect() method then uses a Collector which can combine results in a map. All it needs is one method to extract the key, which is Course::getTitle and one method to extract value which is Function.identity() i.e. the object itself.

We have also used method reference to shorten the key extractor. You can further see What's New in Java 8: Lambdas to learn more about how to convert lambda expression to method reference.

How to convert List<V> into Map<K,V> in Java 8

Btw, there is a catch here. The ordering of elements has lost because Map doesn't guarantee any order. If you want to keep Courses in the same order they appeared in List, we need to use a Map which provides ordering guarantees e.g. LinkedHashMap which keeps elements in the order they are inserted.

We also need to tell this to Collector so that it will collect elements inside a LinkedHashMap rather than a general Map. Let's re-write the code to achieve that:

 Map<String, Course > result = listOfCourses
        .stream()
        .collect(
        Collectors.toMap(Course::getTitle, 
                         Function.identity(), 
                         LinkedHashMap::new));

This looks good now. The order of elements in both List and Map are same now, but there is still one more thing you need to take care to keep this code full proof and pass the test of time.

If you remember, List allows duplicates but if you try to insert a duplicate key in the Map it overrides the values, that would have been a nightmare in this case, but thankfully Java 8 protects you. Instead of silently overwriting a value in such condition, it throws an exception as shown below when it encountered a duplicate element in the source list (see Beginning Java 8 Language Features by Kishori Sharan)

best book to learn Java 8


Though, you can resolve this error by just telling Collector how to resolve collision e.g. what to do when it encounters a duplicate key. It can do nothing and keep the original mapping or it can override and update the value. You can instruct collector whatever you want by providing an extra parameter to the Collectors.toMap() method as shown in following code:

    Map<String, Course > result = listOfCourses
        .stream()
        .collect(
        Collectors.toMap(Course::getTitle, 
                         Function.identity(),
                         (e1, e2) -> e2, 
                         LinkedHashMap::new));

Here in case of a duplicate, we are using the second element as a key into generated LinkedHashMap. You can also choose the first object or just concatenate first and second as per your need.


That's all about how to convert a list of objects List<V> to a map of keys and values e.g. Map<K, V>. It's super easy in Java 8. All you need to know is how to use collect() and Collector. Though you should be mindful of the essential difference between List and Map data structure e.g. one is ordered collection while other is not. Transferring values from List to Map means you will lose the order if you don't preserve them e.g. by using a LinkedHashMap.

Other Java 8 tutorials and resources you may like to explore
The Ultimate Java 8 Tutorial
How to sort HashMap by keys values in Java 8?
How to remove an entry from HashMap in Java?
How to use map() and flatMap() methods in Java?
How to do map-reduce in Java 8?
What's New in Java 8: Lambdas
Beginning Java 8 Language Features by Kishori Sharan

Thanks for reading this article, if you like this article then please share with your friends and colleagues. If you have any questions or feedback then please drop a note. 

3 comments:

  1. List to map code snippet wont work I guess

    ReplyDelete
    Replies
    1. Hello Anonymous, which code snippet is not working? Can you please provide some more details like what error your are getting?

      Delete
  2. In the Java 7 example, do you mean "courses".put(current.getTitle(), current); ?

    ReplyDelete