In this article, we will look at 4 different ways to convert a List to Map in java including one with Java 8, Guava and Apache Commons Library.
List used in this article will be a list of objects and the java class whose objects will be used as list elements is given below.
public class Post { private int id; private String title; // constructor public Post(Integer id, String title) { this.id = id; this.title = title; } // Getters and Setters @Override public String toString() { return "Post [ id=" + id + ", title=" + title + "]"; } public boolean equals(Post p) { return this.id == p.getId(); } }
which has two fields or instance variables and a constructor.
Method 1: Using Java 8
Java 8 streams can be used to convert a list to a map. Following is the code example followed by its explanation.
import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ListToMapconverter { public static void main(String[] args) { Post post1 = getPost(1, "post one"); Post post2 = getPost(1, "post two"); Post post3 = getPost(3, "post three"); Post post4 = getPost(4, "post four"); List<Post> posts = List.of(post1, post2, post3, post4); Map<Integer, Post> map = posts. stream(). collect( Collectors.toMap(Post::getId, post -> post) ); System.out.println(map); } }
In this example,
stream(): returns a stream over list elements.
collect(): Performs operations on the stream of elements using a Collector
.
A Collector
can be created using Collectors
class which contains methods to perform different operations.
One such method is toMap()
that takes two arguments. First argument becomes the key and second becomes the value of resultant map.
So, as the first argument, we pass the id of post object while the post object itself as the second argument.
This prints
{1=Post [ id=1, title=post one],
2=Post [ id=2, title=post one],
3=Post [ id=3, title=post one],
4=Post [ id=4, title=post one]}
Duplicate objects
If the list contains duplicate objects as detected by the equals()
method in Object
class or if there is an overridden equals() method in the class of list elements, then this method will throw an IllegalStateException as below.
Exception in thread “main” java.lang.IllegalStateException: Duplicate key 1 (attempted merging values Post [userId=null, id=1, title=post one] and Post [userId=null, id=1, title=post two])
when the ids of two post objects are the same.
To resolve this, you should use the overloaded toMap()
method which accepts a third argument of type BinaryOperator
interface.
BinaryOperator
contains a method which accepts two arguments and returns a value. Implementation of this interface tells toMap()
how to handle duplicate keys or which object to consider.
Thus, below example will accept the first post object if there are duplicate ids.
Post post1 = getPost(1, "post one"); Post post2 = getPost(1, "post two"); Post post3 = getPost(3, "post three"); Post post4 = getPost(4, "post four"); List<Post> posts = List.of(post1, post2, post3, post4); Map<Integer, Post> map = posts. stream(). collect( Collectors.toMap(Post::getId, post -> post, (p1, p2) -> p1) ); System.out.println(map);
Notice that there two post objects with id 1 and this is the output.
{1=Post [ id=1, title=post one],
3=Post [ id=3, title=post three],
4=Post [ id=4, title=post four]}
which shows that in case of duplicate keys, first object was preferred.
If you change toMap()
as
Map<Integer, Post> map = posts. stream(). collect( Collectors.toMap(Post::getId, post -> post, (p1, p2) -> p2) );
then the output will be
{1=Post [ id=1, title=post two],
3=Post [ id=3, title=post three],
4=Post [ id=4, title=post four]}
Second post object is accepted in this case.
Since BinaryOperator
is a Functional interface, it can be implemented as a Lambda expression such as (p1, p2) -> p2
in this case.
Guava library can be used to convert a List to a Map.
com.google.common.collect.Maps
class has a static uniqueIndex()
method which accepts an iterable and an object of type com.google.common.base.Function
as arguments and returns a map.
Keys of a map are produced by applying the function supplied as a second argument.
Values of map will be same type as the type of list.
Following is a code example
import java.util.List; import java.util.Map; import com.google.common.collect.Maps; public class ListToMapConverter { public static void main(String[] args) { // create post objects Post post1 = getPost(1, "post one"); Post post2 = getPost(2, "post two"); Post post3 = getPost(3, "post three"); Post post4 = getPost(4, "post four"); // create a list from objects List<Post> posts = List.of(post1, post2, post3, post4); // convert list to map Map<Integer, Post> map = Maps.uniqueIndex(posts,Post::getId); System.out.println(map); } // method to create a post object static Post getPost(int id, String title) { return new Post(id, title); } }
In the above example, a list of post objects is created and passed to uniqueIndex()
method. Notice the second argument to uniqueIndex()
is the getId()
method of post objects which becomes the key of the map created from list.
When this map is printed, following is the output
{1=Post [ id=1, title=post one],
2=Post [ id=2, title=post two],
3=Post [ id=3, title=post three],
4=Post [ id=4, title=post four]}
where keys are the id of posts and values are the post objects.
-
- An iterable is anything which can be iterated or which implements
java.lang.Iterable
interface. Since a List implements this interface, it is an iterable. com.google.common.base.Function
is a Functional interface and it extendsjava.util.function.Function
interface.getId()
will be called for each of the Post objects in the list.uniqueIndex()
returns acom.google.common.collect.ImmutableMap
. This implementsjava.util.Map
and hence can be assigned to a reference of this type.
- An iterable is anything which can be iterated or which implements
getId()
should return unique values for all the list objects. If any of the keys are found to be duplicate, uniqueIndex()
will throw an exception.
Duplicate Objects
If two objects produce the same key, then this method will throw an error.
Just modify the above example by changing the id of second post object to 1 and you will get
Exception in thread “main” java.lang.IllegalArgumentException: Multiple entries with same key: 1=Post [ id=1, title=post one] and 1=Post [ id=1, title=post one]. To index multiple values under a key, use Multimaps.index.
For adding guava to the application, include following dependency as per the build tool.
Maven
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>
Gradle
compile group: 'com.google.guava', name: 'guava', version: '29.0-jre'
Apache Commons Collections library can be used to convert a list to a map using its
MapUtils
class.This class belongs to
org.apache.commons.collections4
package and has a method populateMap()
which takes 3 arguments1. A map object which will be populated with elements from the list,
2. An iterable. Again, a list is an iterable, and
3. An object that implements its
Transformer
interface.
Transformer is a Functional interface having a single method transform()
which accepts and input object and returns a transformed object.
Note that the transformation logic needs to be written as required.
Example program using MapUtils
is given below.
import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections4.MapUtils; import org.apache.commons.collections4.Transformer; public class ListToMapConverter { public static void main(String[] args) { Post post1 = getPost(1, "post one"); Post post2 = getPost(2, "post two"); Post post3 = getPost(3, "post three"); Post post4 = getPost(4, "post four"); List<Post> posts = List.of(post1, post2, post3, post4); Map<Integer, Post> map = new HashMap<>(); MapUtils.populateMap(map, posts, new Transformer<Post, Integer>() { @Override public Integer transform(Post post) { return post.getId(); } }); System.out.println(map); } }
Since Transformer
is an interface, it can be implemented as an anonymous inner class as in this example.
Value returned by transform()
is used as a key for the map entries.
Thus, for converting list objects to a map, transform()
should accept the object and return the value that should be used as the key.
Therefore, transform()
in above example accepts a Post object and returns its id.
This prints
{1=Post [ id=1, title=post one],
2=Post [ id=2, title=post two],
3=Post [ id=3, title=post three],
4=Post [ id=4, title=post four]}
Also, since Transformer
is a Functional interface, transform()
can be implemented using a Lambda expression.
Thus, populateMap()
can also be written as
MapUtils.populateMap(map, posts, (post) -> post.getId());
Add Apache Commons dependency to your project as per the appropriate build tool.
Maven
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.4</version> </dependency>
Gradle
compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'
Duplicate Objects
If there are multiple objects which have the same key, then Apache Commons, overwrites the later one just like the default map behavior.
So, if the id of first and second post objects are the same, then below will be the output of this method.
{1=Post [ id=1, title=post two],
3=Post [ id=3, title=post three],
4=Post [ id=4, title=post four]}
Method 4: Iterating List
This is the simplest method that involves iterating over the list of objects and adding it to a map with key and value as required.
Example code follows
import java.util.HashMap; import java.util.List; import java.util.Map; public class ListToMapConverter { public static void main(String[] args) { Post post1 = getPost(1, "post one"); Post post2 = getPost(1, "post two"); Post post3 = getPost(3, "post three"); Post post4 = getPost(4, "post four"); List<Post> posts = List.of(post1, post2, post3, post4); Map<Integer, Post> map = new HashMap<Integer, Post>(); // iterate over list for(Post post: posts) { // add to map map.put(post.getId(), post); } System.out.println(map); } }
List of post objects is created with an enhanced for loop. In every iteration, an entry is added to a map with id of post as key and post object as value.
Hope this article was useful.