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.

Method 2: Using guava library
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.

A Tweak inside...
    • 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 extends java.util.function.Function interface.
    • getId() will be called for each of the Post objects in the list.
    • uniqueIndex() returns a com.google.common.collect.ImmutableMap. This implements java.util.Map and hence can be assigned to a reference of this type.

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'

Method 3: Using Apache Commons
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 arguments
1. 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.

0
Liked the article ? Spread the word...