June 24, 2024

Java Stream Collectors.groupingBy() Examples

In this tutorial we’ll see some examples of Collectors.groupingBy() method in Java Stream API. Collectors.groupingBy() method works similarly to "group by" statement in SQL which groups the elements as per the specified columns. This method also groups the elements according to the passed property and returns grouped result as a Map.

There are three overloaded Collectors.groupingBy() method-

  • Collector<T,?,Map<K,List>> groupingBy(Function<? super T,? extends K> classifier)- This method groups elements according to a classification function and returns the results in a Map. The collector produces a Map<K, List> where key specifies a group and List contains the elements which map to the associated key
  • Collector<T,?,Map<K,D>> groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)- In this groupingBy() method first elements are grouped according to a passed classification function and then reduction operation is performed on the values associated with a given key using the Collector passed as second argument.
  • Collector<T,?,M> groupingBy(Function<? super T,? extends K> classifier, Supplier mapFactory, Collector<? super T,A,D> downstream)- In this variant first elements are grouped according to a passed classification function then reduction operation is performed on the values associated with a given key using the Collector passed as second argument. The resulting Map produced by the Collector is created with the supplied factory function.

Note that the returned Collector, in all the above methods, is not concurrent. There is a groupingByConcurrent() method with the same 3 overloaded methods which may offer better parallel performance. In case of groupingByConcurrent() methods a ConcurrentMap is returned.

Collectors.groupingBy() Java examples

For the example we’ll use the objects of the Student class.

public class Student {
  private int rollNo;
  private String name;
  private String stream;
  private int marks;
  Student(int rollNo, String name, String stream, int marks){
    this.rollNo = rollNo;
    this.name = name;
    this.stream = stream;
    this.marks = marks;
  }
  public int getRollNo() {
    return rollNo;
  }
  public void setRollNo(int rollNo) {
    this.rollNo = rollNo;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getStream() {
    return stream;
  }
  public void setStream(String stream) {
    this.stream = stream;
  }
  public int getMarks() {
    return marks;
  }
  public void setMarks(int marks) {
    this.marks = marks;
  }
}

1. If we need to group student according to the subject stream we can use the first Collectors.groupingBy() method where only a single argument classifier function is passed.

public class GroupingDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
            new Student(2, "Ram", "Science", 99),
            new Student(3, "Priscilla", "Art", 68),
            new Student(4, "Mahesh", "Art", 62),
            new Student(5, "Scott", "Commerce", 72));
    Map<String, List<Student>> names = studentList.stream()
        .collect(Collectors.groupingBy(Student::getStream));
    // Iterating the returned Map
    names.entrySet().forEach(es->{System.out.println("Stream- " + es.getKey());
    System.out.println("**Students**");
    es.getValue().forEach(e->System.out.println(e.getName()));});

  }
}
Output
Stream- Art
**Students**
Priscilla
Mahesh
Stream- Science
**Students**
Peter
Ram
Stream- Commerce
**Students**
Scott

2. If you want the count of students in each stream then you need to group student according to the subject stream and also pass Collectors.counting() (which returns a collector) as a second argument.

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupingDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
            new Student(2, "Ram", "Science", 99),
            new Student(3, "Priscilla", "Art", 68),
            new Student(4, "Mahesh", "Art", 62),
            new Student(5, "Scott", "Commerce", 72));
    Map<String, Long> names = studentList.stream()
        .collect(Collectors.groupingBy(Student::getStream, Collectors.counting()));

    names.entrySet().forEach(es-> {
              System.out.println("Stream- " + es.getKey() + 
                  " Number of Students- " + es.getValue());
              });
  }
}
Output
Stream- Art Number of Students- 2
Stream- Science Number of Students- 2
Stream- Commerce Number of Students- 1

3. If you want the Max marks in each stream then you need to group student according to the stream and also pass Collectors.maxBy (which returns a collector) as a second argument.

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class GroupingDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
            new Student(2, "Ram", "Science", 99),
            new Student(3, "Priscilla", "Art", 68),
            new Student(4, "Mahesh", "Art", 62),
            new Student(5, "Scott", "Commerce", 72));
    Map<String, Optional<Student>> names = studentList.stream()
        .collect(Collectors.groupingBy(Student::getStream, Collectors.maxBy(Comparator.comparingInt(Student::getMarks))));

    names.entrySet().forEach(es-> {
              System.out.println("Stream- " + es.getKey() + 
                  " Student Name- " + es.getValue().get().getName() +
                  " Marks- " + es.getValue().get().getMarks());
              });
  }
}
Output
Stream- Art Student Name- Priscilla Marks- 68
Stream- Science Student Name- Ram Marks- 99
Stream- Commerce Student Name- Scott Marks- 72

4. If you want sorting to be done by keys you can return a TreeMap as a result of using groupingBy() method. In that case you have to pass one more argument which is of type Supplier and acts as a factory function.

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class GroupingDemo {

  public static void main(String[] args) {
    List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
            new Student(2, "Ram", "Science", 99),
            new Student(3, "Priscilla", "Art", 68),
            new Student(4, "Mahesh", "Art", 62),
            new Student(5, "Scott", "Commerce", 72));
    Map<String, Set<String>> names = studentList.stream()
        .collect(Collectors.groupingBy(Student::getStream, TreeMap::new, Collectors.mapping(Student::getName, Collectors.toSet())));

    names.entrySet().forEach(es-> {
              System.out.println("Stream- " + es.getKey());
              System.out.println("**Students**");
                es.getValue().forEach(name -> System.out.println(name));
                });
  }
}
Output
Stream- Art
**Students**
Priscilla
Mahesh
Stream- Commerce
**Students**
Scott
Stream- Science
**Students**
Peter
Ram

Collectors.groupingByConcurrent() Java example

1. If you need to group student according to the stream in parallel we can use the Collectors.groupingByConcurrent() method where only a single argument classifier function is passed.

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

public class GroupingDemo {

    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
                new Student(2, "Ram", "Science", 99),
                new Student(3, "Priscilla", "Art", 68),
                new Student(4, "Mahesh", "Art", 62),
                new Student(5, "Scott", "Commerce", 72));
        ConcurrentMap<String, List<Student>> names = studentList.parallelStream()
            .collect(Collectors.groupingByConcurrent(Student::getStream));
        // Iterating the returned Map
        names.entrySet().forEach(es->{System.out.println("Stream- " + es.getKey());
        System.out.println("**Students**");
        es.getValue().forEach(e->System.out.println(e.getName()));});

    }
}

That's all for the topic Java Stream Collectors.groupingBy() Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment