One of the common tasks in Java programming is to convert a list to a map and I have written about this in the 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 a map because they are two different data structures and have completely different properties. For example, the List interface in Java 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. This means a List with duplicates cannot be directly converted into Map without handling the duplicate values properly.
Similarly, another concern is the order of elements. 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 chasing 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 a map. So, you should be mindful of these general details while converting a list to a map in Java. This is true irrespective of the Java version.
Btw, if you are new to the Java world, I suggest you first go through a comprehensive course on Java-like The Complete Java Masterclass on Udemy. It not only provides organized and structure learning but also you will learn more in less time. It's also one of the most up-to-date courses, recently updated for Java 11 features.
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?
Btw, if you are new to the Java world, I suggest you first go through a comprehensive course on Java-like The Complete Java Masterclass on Udemy. It not only provides organized and structure learning but also you will learn more in less time. It's also one of the most up-to-date courses, recently updated for Java 11 features.
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? Example
It's easy in Java, 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 like 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 a 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 a method reference.
Btw, there is a catch here. The ordering of elements has been lost because Map doesn't guarantee any order. If you want to keep Courses in the same order they appeared in the List, we need to use a Map that 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 the same now, but there is still one more thing you need to take care of 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 Modern Java in Action )
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 the collector whatever you want by providing an extra parameter to the Collectors.toMap() method as shown in the 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 to generated LinkedHashMap. You can also choose the first object or just concatenate the 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 the collect() and Collector class of Stream API to do this task.
Though you should be mindful of the essential difference between List and Map data structure like one is ordered collection while the other is not. Transferring values from List to Map means you will lose the order if you don't preserve them like by using a LinkedHashMap.
Other Java tutorials and Resources you may like to explore
- 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 do you map-reduce in Java 8?
- 10 Java 8 Stream Interview Questions
- 5 Best Java Functional Programming Courses
- 10 Examples of Stream in Java 8
- 10 Free Courses to become a full-stack Developer
- Top 5 Courses to learn Lambda and Stream in Java
- 7 Best Courses to learn Java Collection Framework
- 21 Books Java Developers should Read this year
Thanks for reading this article, if you like this article then please share it with your friends and colleagues. If you have any questions or feedback then please drop a note.
P. S. - If you are looking for some free courses to learn new features introduced in Java 8 and Java 9 then this list of free Java 8 and Java 9 Courses can be very useful.
List to map code snippet wont work I guess
ReplyDeleteHello Anonymous, which code snippet is not working? Can you please provide some more details like what error your are getting?
DeleteIn the Java 7 example, do you mean "courses".put(current.getTitle(), current); ?
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete