Monday, January 17, 2022

Java Stream - Convert Stream to Map

In this tutorial you’ll see how to convert a Java Stream to Map using collector method and the utility methods provided by Collectors like Collectors.toMap() and Collectors.groupingBy().

Collectors.toMap() method in Java Stream

Collectors.toMap() is overloaded and it has following variants-

1. Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)- Accumulates stream elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements.

Mapping functions which are passed as parameter are-

  • keyMapper- a mapping function to produce keys
  • valueMapper- a mapping function to produce values

Method returns a Collector which collects elements into a Map

2. Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)- This variant of toMap() is used if the mapped keys contain duplicates, in that case the value mapping function is applied to each equal element and the results are merged using the provided merging function.

3. Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)- This toMap() method is used if you want to specify the type of Map in which elements are stored. The Map is created by a provided supplier function.

Collectors.toMap() Java examples

1. In the following example a list of employees is collected to a Map where key is the employeeID and value is the employee object itself.

Employee class

public class Employee {
  private String empId;
  private int age;
  private String name;
  private char gender;
  private int salary;
  Employee(String empId, int age, String name, char gender, int salary){
    this.empId = empId;
    this.age = age;
    this.name = name;
    this.gender = gender;
    this.salary = salary;
  }
  public String getEmpId() {
    return empId;
  }

  public int getAge() {
    return age;
  }

  public String getName() {
    return name;
  }

  public char getGender() {
    return gender;
  }

  public int getSalary() {
    return salary;
  }
  @Override
  public String toString() {
    return "Emp Id: " +  getEmpId() + " Name: " + getName() + " Age: " + getAge();
  }
}
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StreamToMap {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 12000), 
        new Employee("E006", 28, "Amy", 'F', 14000)); 
    Map<String, Employee> empMap = empList.stream()
                .collect(Collectors.toMap(Employee::getEmpId, Function.identity()));
    // To check type of Map
    System.out.println(empMap.getClass().getName());
    System.out.println(empMap);

  }

}

Output

java.util.HashMap
{E005=Emp Id: E005 Name: Anuj Age: 32, E006=Emp Id: E006 Name: Amy Age: 28, E001=Emp Id: E001 Name: Ram Age: 40, 
E002=Emp Id: E002 Name: Shelly Age: 35, E003=Emp Id: E003 Name: Mark Age: 24, E004=Emp Id: E004 Name: Ritu Age: 37}

Though the Javadocs say “There are no guarantees on the type, mutability, serializability, or thread-safety of the Map returned.” but usually HashMap is returned as checked in the example by printing type of the Map. To specify different Map type you can use the toMap() method with four arguments.

2. In the following example a list of employees is collected to a Map where key is the gender of the employee and value is the employee name. Here we’ll have duplicates in the key value so we can use the toMap() method with three arguments and specify the merging function which is of type BinaryOperator functional interface.

public class StreamToMap {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 12000), 
        new Employee("E006", 28, "Amy", 'F', 14000)); 
    Map<Character, String> empMap = empList.stream()
                .collect(Collectors.toMap(Employee::getGender, Employee::getName, (a, b) -> a + ", " + b));
    System.out.println(empMap);
  }
}

Output

{F=Shelly, Ritu, Amy, M=Ram, Mark, Anuj}

3. In case you want to collect to another type of Map for example TreeMap then you can use the toMap() method with four parameters.

public class StreamToMap {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 12000), 
        new Employee("E006", 28, "Amy", 'F', 14000)); 
    Map<Character, String> empMap = empList.stream()
                .collect(Collectors.toMap(Employee::getGender, Employee::getName, (a, b) -> a + ", " + b, TreeMap::new));
    // To check type of Map
    System.out.println(empMap.getClass().getName());
    System.out.println(empMap);
  }
}

Output

java.util.TreeMap
{F=Shelly, Ritu, Amy, M=Ram, Mark, Anuj}

As you can see now type of the Map is TreeMap.

Stream to Map using Collectors.groupingBy()

Collectors.groupingBy() method returns a collector that produces a Map<K, List<T>> whose keys are the values resulting from applying the classification function to the input elements and corresponding values for each groups are stored in Lists. In the scenario of duplicate keys groupingBy() method is also one option to group elements into different lists.

For example if you have a list of employees and you want to group objects on the basis of gender.

public class StreamToMap {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 12000), 
        new Employee("E006", 28, "Amy", 'F', 14000)); 
    Map<Character, List<Employee>> empMap = empList.stream()
                .collect(Collectors.groupingBy(Employee::getGender));
    System.out.println(empMap);
  }
}

Output

java.util.HashMap
{F=[Emp Id: E002 Name: Shelly Age: 35, Emp Id: E004 Name: Ritu Age: 37, Emp Id: E006 Name: Amy Age: 28], 
M=[Emp Id: E001 Name: Ram Age: 40, Emp Id: E003 Name: Mark Age: 24, Emp Id: E005 Name: Anuj Age: 32]}

That's all for this topic Java Stream - Convert Stream to Map. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream - Convert Stream to List
  2. Java Stream - Convert Stream to Set
  3. Java Stream - Convert Stream to Array
  4. Java Stream - sorted() With Examples

You may also like-

  1. Difference Between Comparable and Comparator in Java
  2. Java Exchanger With Examples
  3. Why Class Name And File Name Should be Same in Java
  4. How to Read Input From Console in Java
  5. Java OOP Interview Questions And Answers
  6. Java Program to Read File in HDFS
  7. Spring Email Scheduling Example Using Quartz Scheduler
  8. Check if String Present in Another String in Python

No comments:

Post a Comment