In this post, we will see how to convert List to Map using Stream in java 8.
Collectors’s toMap()
can be used with stream to convert List to Map in java.
Consider a class Named Movie
which have 3 fields – id
, name
and genre
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
package org.arpit.java2blog; public class Movie { private String name; private String genre; public Movie(String name, String genre) { super(); this.name = name; this.genre = genre; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } @Override public String toString() { return "Movie [name=" + name + ", genre=" + genre + "]"; } } |
Create a list of movies and convert with to map with name
as key and genre
as value.
Create a class Named ConvertListToMapMain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
package org.arpit.java2blog; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ConvertListToMapMain { public static void main(String[] args) { List<Movie> moviesList=getListOfMovies(); Map<String, String> moviesMap = moviesList.stream() .collect(Collectors.toMap((m)->m.getName(), (m)->m.getGenre())); System.out.println(moviesMap); } public static List<Movie> getListOfMovies() { List<Movie> moviesList=new ArrayList<>(); Movie m1=new Movie("3 idiots","Comedy"); Movie m2=new Movie("Interstellar","SciFi"); Movie m3=new Movie("Forest gump","Comedy"); Movie m4=new Movie("Matrix","SciFi"); Movie m5=new Movie("The Hangover","Comedy"); moviesList.add(m1); moviesList.add(m2); moviesList.add(m3); moviesList.add(m4); moviesList.add(m5); return moviesList; } } |
Output:
{Interstellar=SciFi, Matrix=SciFi, The Hangover=Comedy, Forest gump=Comedy, 3 idiots=Comedy}
moviesMap
contains name as key and genre as value.
We can also use method references instead of lamda expressions.
|
Map<String, String> moviesMap = moviesList.stream() .collect(Collectors.toMap(Movie::getName, Movie::getGenre())); |
What happens in case of a duplicate key?
In case, duplicate keys are present, then it will throw java.lang.IllegalStateException
exception.
Let’s say we have two movies with name Matrix
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public static List<Movie> getListOfMovies() { List<Movie> moviesList=new ArrayList<>(); Movie m1=new Movie("3 idiots","Comedy"); Movie m2=new Movie("Interstellar","SciFi"); Movie m3=new Movie("Forest gump","Comedy"); Movie m4=new Movie("Matrix","SciFi"); Movie m5=new Movie("Matrix","Comedy"); moviesList.add(m1); moviesList.add(m2); moviesList.add(m3); moviesList.add(m4); moviesList.add(m5); return moviesList; } |
When you run ConvertListToMapMain again, you will get below output:
Output:
Exception in thread “main” java.lang.IllegalStateException: Duplicate key Sci Fi
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at org.arpit.java2blog.ConvertListToMapMain.main(ConvertListToMapMain.java:14)
To resolve this issue, we need to pass merge BinaryOperator
function to Collectors.toMap()
.
|
Map<String, String> moviesMap = moviesList.stream() .collect(Collectors.toMap((m)->m.getName(), (m)->m.getGenre(), (oldValue,newValue) -> newValue )); |
When you run ConvertListToMapMain again, you will get below output:
Output:
{Matrix=Comedy, Interstellar=SciFi, Forest gump=Comedy, 3 idiots=Comedy}
What if you want specific Map such as TreeMap?
You can pass constructor method reference to Collectors.toMap()
to get specific map.
|
Map<String, String> moviesMap = moviesList.stream() .collect(Collectors.toMap( (m)->m.getName(), (m)->m.getGenre(), (oldValue,newValue) -> newValue ,TreeMap::new )); |
When you run ConvertListToMapMain again, you will get below output:
Output:
{3 idiots=Comedy, Forest gump=Comedy, Interstellar=SciFi, Matrix=Comedy}
As you can see, we have TreeMap
which is sorted by name.
Get multiMap with Collectors.groupingBy()
In case, you want movie grouped by genre as key and name as value, then you can use Collectors.groupingBy() and Collectors.mapping as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
package org.arpit.java2blog; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ConvertListToMapMain { public static void main(String[] args) { List<Movie> moviesList=getListOfMovies(); Map<String, List<String>> moviesGenMap = moviesList.stream() .collect(Collectors.groupingBy(Movie::getGenre ,Collectors.mapping(Movie::getName, Collectors.toList()))); System.out.println(moviesGenMap); } public static List<Movie> getListOfMovies() { List<Movie> moviesList=new ArrayList<>(); Movie m1=new Movie("3 idiots","Comedy"); Movie m2=new Movie("Interstellar","SciFi"); Movie m3=new Movie("Forest gump","Comedy"); Movie m4=new Movie("Matrix","SciFi"); Movie m5=new Movie("The Hangover","Comedy"); moviesList.add(m1); moviesList.add(m2); moviesList.add(m3); moviesList.add(m4); moviesList.add(m5); return moviesList; } } |
Output:
{Comedy=[3 idiots, Forest gump, The Hangover], SciFi=[Interstellar, Matrix]}
As you can see, moviesGenMap
has genre as key and list of movies name as value.
Excercise
Given a list of Employee
objects, you need to convert list to Map with name
as key and age
as value. In case, there are duplicate keys, then you need to preserve old value.
Here is the definition of Employee class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
package org.arpit.java2blog; public class Employee { String name; int age; public Employee(String name,int age) { super(); this.name = name; this.age=age; } 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; } } |
We can use
Collectors.toMap()
to convert list of employees to Map. As we need to preserve old values in case of duplicate key, we have used merge function as
(oldValue,newValue)->oldValue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
package org.arpit.java2blog; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ExcerciseListToMap { public static void main(String[] args) { List<Employee> eList=getListOfEmployees(); Map<String, Integer> mapNameToAge = eList.stream() .collect(Collectors.toMap(Employee::getName ,Employee::getAge ,(oldValue,newValue)->oldValue )); System.out.println(mapNameToAge); } private static List<Employee> getListOfEmployees() { List<Employee> listOfEmployees=new ArrayList<>(); Employee e1=new Employee("Amit",24); Employee e2=new Employee("Anchit",27); Employee e3=new Employee("Vaibhav",32); Employee e4=new Employee("Amit",22); listOfEmployees.add(e1); listOfEmployees.add(e2); listOfEmployees.add(e3); listOfEmployees.add(e4); return listOfEmployees; } } |
Output:
{Amit=24, Vaibhav=32, Anchit=27}
That’s all about Java stream List to Map
Let us know if this post was helpful. Feedbacks are monitored on daily basis. Please do provide feedback as that\'s the only way to improve.
Well explained