Java 8 Collectors toMap

May 09, 2022
XBLOG Java8



Introduction

It is not very easy in programs to manipulate, transfer, or operate the data, especially when dealing with data on a large scale. Java shines in this area as it offers tons of features for dealing with data. A great example would be the variety of data structures, classes and methods available in Java. One such class specifically used for applying reduction operations on data is the Java collectors class.

Java collectors class

Introduced in Java 8, the Java collectors class is a final class in Java that extends the object class. It offers methods for reduction operations, such as manipulating elements present in a collection as per different rules and criteria. It returns a collector that produces the arithmetic mean of the operations applied to the elements in the collection.

explore new Java roles

Just for some basic understanding, the JDK offers various basic to complex operations like sum, min or max that return a single value by manipulating multiple input values. Such operations can also return a collection instead of a value. These are called reduction operations.

Collectors class offers a series of methods to work with reduction operations. In this article, we will be looking at one very specific method, the Java Collectors toMap.

We will discuss the significance of this method, how it works, its uses in your Java code, and some examples.

Java collectors toMap

The toMap method is a static method from the Java collectors class that returns a collector that produces a new Map instance. That newly created Map contains all the keys, and values are calculated by applying the provided mapping functions to the input elements.

All the provided keys have to be unique, and a repeating key would result in an IllegalStateException whenever a collection operation is performed. Java Collectors toMap is commonly used to transform a data structure like a list or an array into a Map by transferring all the elements into a newly created Map.

 

Below mentioned is the syntax of Java collectors toMap method:

toMap(Function keyMapper, Function valueMapper)

 

The basic Java collectors toMap method accepts the following parameters:

keyMapper: A mapping function that produces and returnc keys.

valueMapper: Another mapping function that produces values.

Java collectors toMap example: Transforming an array to Map

The code below demonstrates the transformation of a 2d array into a map using the Java Collector toMap method. The IDs and names of some students are to be transferred to a Map.

01. import java.util.stream.Collectors;
02. import java.util.stream.Stream;
03. import java.util.*;  
04. public class students {
05.   public static void main(String[] args)
06.   {  
07.    Stream<String[]> myStr = Stream.of(new String[][] { { "std-001", "Sam Wilson" },
08.                                        { "std-002", "Kate Moore" },
09.                                        { "std-003", "Jim Martin" } });
10.   Map<String, String> myMap = myStr.collect(Collectors.toMap(x -> x[0], x -> x[1]));
11.
12.      // Printing the returned Map
13.      System.out.println("My map : " + myMap);
14.    }
15.  }

 

Output:

My map : {std-001=Sam Wilson, std-002=Kate Moore, std-003=Jim Martin}

How to deal with the repeating key values?

The example demonstrated above had no issue as all the keys were unique, but what would have happened if there were duplicate keys existing in the array?

As mentioned earlier, this would have resulted in an IllegalStateException. It is very likely in the case when keys are either to be entered by the user or generated by a hash algorithm.

 

For instance, if there would have been the same key for multiple values in the previous example like this:

01. Stream<String[]> myStr = Stream.of(new String[][] { { "std-001", "Sam Wilson" },
02.                                    { "std-002", "Kate Moore" },
03.                                    { "std-001", "Jim Martin" } });
04. Map<String, String> myMap = myStr.collect(Collectors.toMap(x -> x[0], x -> x[1]));

 

The output would be as shown below:

Caused by: java.lang.IllegalStateException: Duplicate key std-001

          at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)

          at java.util.HashMap.merge(HashMap.java:1253)

          at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)

          at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)

          at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)

          at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)

          at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)

          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:499)

          at com.logicbig.example.collectors.students.main(ToMapExample1.java:17)

          ... 5 more

To resolve it, we need to overload the Java collectors toMap method by passing an additional parameter, the merge function.

 

See the overloaded method syntax below:

toMap(Function keyMapper, Function valueMapper, BinaryOperator<U> mergeFunction)

The added merge function resolves any collision between two or more values due to the same key values. All such values are combined and associated with the same key.

 

See this code shown below. The code from the previous example is altered by including a case where the key is repeated for two values, and the overloaded method is used to deal with it:

01. import java.util.stream.Collectors;
02. import java.util.stream.Stream;
03. import java.util.*;
04. 
05. public class students {
06.    public static void main(String[] args)
07.   {
08. 
09.     Stream<String[]> myStr = Stream.of(new String[][] { { "std-001", "Sam Wilson" },
10.                                            { "std-002", "Kate Moore" },
11.                                            { "std-001", "Jim Martin" } });
12.
13.        Map<String, String> myMap = myStr.collect(Collectors.toMap(x -> x[0], x -> x[1], (a, b) -> a + ", " + b));
14.
15.         // Printing the returned Map
16.        System.out.println("My map:" + map);
17.    }
18. }

 

Output:

My Map: {std-002=Kate Moore, std-001=Sam Wilson, Jim Martin}

Using Java collectors toMap method for other Map types

There are various Maps available in Java, and each exhibits different properties and features. The Java collectors toMap method returns a HashMap by default. Still, it is also possible to return a different type, such as linked HashMap or concurrent HashMap, by overloading this method.

 

The syntax for overloading the Java collectors toMap method for implementing different types of Maps is shown below:

toMap(Function keyMapper, Function valueMapper, BinaryOperator<U> mergeFunction, Supplier mapSupplier)

The new parameter passed is the mapSupplier function. It returns a new, empty map of your selected type to insert the results.

The Supplier is the interface of the Java.util.Function class. Therefore, as it is a functional interface, it is used as the assignment target for a method reference. For instance, If you want to return the Linked HashMap, you will have to pass the Supplier as LinkedHashMap::new.

 

See the code below demonstrates how you can return a linked HashMap instead of a simple HashMap. The method is also overloaded for collision resolution in case of the repeating key values:

01. import java.util.stream.Collectors;
02. import java.util.stream.Stream;
03. import java.util.*;  
04.
05. public class students {
06.  
07. public static void main(String[] args)
08.  {
09.  Stream<String[]> myStr = Stream.of(new String[][] { { "std-001", "Sam Wilson" },
10.                                          { "std-002", "Kate Moore" },
11.                                          { "std-001", "Jim Martin" } });  
12.      LinkedHashMap<String, String>
13.      myMap = myStr.collect(Collectors.toMap(x -> x[0], x -> x[1], (a, b) -> a + ", " + b, LinkedHashMap::new));
14.
15.      // Printing the returning Map
16.      System.out.println("My Linked Hash Map:" + myMap);
17.    }
18. }

 

Output:

My Linked Hash Map:{std-001=Sam Wilson, Jim Martin, std-002=Kate Moore}

Most of the code is the same as before, and it is just overloaded by passing the new Map type. For returning any other type of Map such as concurrent HashMap, you only need to change the “LinkedHashMap::new” parameter with “ConcurrentHashMap::new.”

How to sort the elements in the Map?

Sorting the elements in a map can be a tricky task to perform. You would need to sort it as per the criteria in the previous data structure, which could be difficult to identify. Thankfully, there is a very easy approach to sort the returning Map when using the Java collectors toMap method. When selecting the returning Map, you will have to use the TreeMap as a mapSupplier parameter.

Using the TreeMap out of all different types is that one of the properties of a TreeMap type is to automatically sort the elements according to the keys’ natural ordering. To summarize it, you will not need to write any additional code to sort the elements explicitly.

See Also: Guide To Java 8 forEach Method With Example

Wrapping it up

Java collectors toMap proves to be a very useful method for transferring all the data from a different data structure to a map. It implicitly performs all the tasks such as creating a map, transferring the elements, and sorting the elements. We looked in all the overloaded methods of Java Collectors toMap. This method can be easily overloaded to avoid exceptions due to collision by the repeated key values. Another overloaded method is used when you have to use different types of Maps other than simple HashMap.

new Java jobs



author

admin


Candidate signup

Create a free profile and find your next great opportunity.

JOIN NOW

Employer signup

Sign up and find a perfect match for your team.

HIRE NOW

How it works

Xperti vets skilled professionals with its unique talent-matching process.

LET’S EXPLORE

Join our community

Connect and engage with technology enthusiasts.

CONNECT WITH US