Convert a List of objects into a Map
Converting a List to a Map is common when building lookup tables or grouping data. The Stream API's Collectors.toMap() provides a clean functional approach: you specify key and value extractor functions. This article covers the basic form, handling duplicate keys with a merge function (which otherwise throws IllegalStateException), and preserving insertion order with a LinkedHashMap.
In this article we are going to look at different ways of converting a ListMap
StreamList#parallelStream()CopyOnWriteArrayListUsing Stream API collect method
From Java 8 onwards, you can use Java Stream API to collect elements from a Collection like List.
In order to follow below example, create a class called Person
public class Person {
private String name;
private int age;
private String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getGender() {
return gender;
}
@Override
public String toString() {
return "[name : " +name + " , age : "+age + ", gender : "+gender+"]";
}
}
Let's write a program to convert List<Person>Map<String, Person>keyvalue
public static void listToMap(){
List<Person> persons = List.of(
new Person("Person A", 29, "Male"),
new Person("Person B", 32, "Female"),
new Person("Person C", 45, "Male"),
new Person("Person D", 39, "Female")
);
Map<String, Person> nameToPersonMap= persons.stream().collect(Collectors.toMap(Person::getName, Function.identity()));
//to print each entry in a line, we are using iterator and print it
nameToPersonMap.entrySet().iterator().forEachRemaining(System.out::println);
}
Explanation of above code:
-
First we called
method onstream() that returns aList<Person> objects.Stream<Person> -
Then on the
object, we calledStream method with a Collector that collects into a Map usingcollect() which is an utility method that takes two arguments, first one is the key mapper and second one is the value mapper, in this caseCollectors.toMap(key mapper function, value mapper function) is person name, so we are passingkey and value is the Person objectPerson::getName returns the value itself.Function.identity()
Running it prints:
Person A=[name : Person A , age : 29, gender : Male]
Person B=[name : Person B , age : 32, gender : Female]
Person C=[name : Person C , age : 45, gender : Male]
Person D=[name : Person D , age : 39, gender : Female]
How to handle duplicates keys
Say when are you are converting ListMapkey
For example, if you run the previous program with gender as keyvaluePerson
Map<String, List<Person>> nameToPersonMap= persons.stream().collect
(Collectors.toMap(Person::getGender,
person-> {
List<Person> personList = new ArrayList<>();
personList.add(person);
return personList;
}
));
When you run the above code, then it will throw below exception, because Person A and Person C have same keys.
Exception in thread "main" java.lang.IllegalStateException: Duplicate key Male (attempted merging values [[name : Person A , age : 29, gender : Male]] and [[name : Person C , age : 45, gender : Male]])
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
In this case, you can pass a mergejava.util.function.BinaryOperatoroldValuenewValue
public static void listToMap_HandleDuplicates(){
List<Person> persons = List.of(
new Person("Person A", 29, "Male"),
new Person("Person B", 32, "Female"),
new Person("Person C", 45, "Male"),
new Person("Person D", 39, "Female")
);
Map<String, List<Person>> nameToPersonMap= persons.stream().collect
(Collectors.toMap(Person::getGender,
person-> {
List<Person> personList = new ArrayList<>();
personList.add(person);
return personList;
}, (oldValue, newValue) ->{
oldValue.addAll(newValue);
return oldValue;
}
));
//to print each entry in a line, we are using iterator and print it
nameToPersonMap.entrySet().iterator().forEachRemaining(System.out::println);
}
Running it prints:
Female=[[name : Person B , age : 32, gender : Female], [name : Person D , age : 39, gender : Female]]
Male=[[name : Person A , age : 29, gender : Male], [name : Person C , age : 45, gender : Male]]
Choose your own Map implementation
In previous examples, we have used Collectors#toMapHashMapCollectors#toConcurrentMapConcurrentHashMap
Map<String, Person> nameToPersonMap= persons.stream()
.collect(Collectors.toConcurrentMap(Person::getName, Function.identity()));
You can also pass your choice of Map implementation as well, in below example we are passing LinkedHashMap
public static void listToMap_WithCustomMapImpl() {
List<Person> persons = List.of(
new Person("Person A", 29, "Male"),
new Person("Person B", 32, "Female"),
new Person("Person C", 45, "Male"),
new Person("Person D", 39, "Female")
);
Map<String, Person> nameToPersonMap= persons.stream()
.collect(Collectors.toMap(Person::getName, Function.identity(),
(oldValue, newValue)->oldValue, LinkedHashMap::new ));
//to print each entry in a line, we are using iterator and print it
nameToPersonMap.entrySet().iterator().forEachRemaining(System.out::println);
}
Running it prints:
Person A=[name : Person A , age : 29, gender : Male]
Person B=[name : Person B , age : 32, gender : Female]
Person C=[name : Person C , age : 45, gender : Male]
Person D=[name : Person D , age : 39, gender : Female]
Above example complete source code can be found here on
Similar Articles
-
Convert a Map into a List of objects using Java Stream API (from Java 8 onwards) -
Filter and find an element from a List using Java Stream API (from Java 8 onwards) -
How to remove elements from a List -
How to remove elements from a List while iterating -
How to loop through elements of List in different ways