JB Header
Java 8 Finding max/min with Collectors | maxBy, minBy methods tutorial with examples
Java 8 Determining maximum and minimum with Collectors tutorial explains, with examples, how to use the predefined Collectors returned by java.util.Stream.Collectors class' maxBy() and minBy() methods to find the maximum and minimum element of a given Stream. The tutorial starts off with the formal definition of the maxBy(), minBy() methods, then explains their working, and finally shows the methods' usage via a Java code example.
(Note - This tutorial assumes that you are familiar with basics of Java 8 CollectorsRead Tutorial explaining basics of Java 8 Collectors.) Predefined Collectors returned by Collectors.maxBy()/Collectors.minBy() methods Collectors.maxBy()/minBy() methods are defined with the following signatures -
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
AND
public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
Where,
     - input parameter is an instance of Comparator of type T
     - output is a Collector, acting on a Stream of elements of type T, with its finisherClick to Read tutorial on 4 components of Collectors incl. 'finisher' returning the maximum(for maxBy()) or minimum(for minBy()) value from all the elements of the stream as an Optional value

The predefined collectors returned by Collectors class' maxBy()/minBy() methods get the maximum/minimum elements in the stream on which they are invoked using the Stream.collect() method. The max/min value is returned as an Optional value. This is because in case the stream has no elements then to avoid sending a bare null value which can result in a NullPointerException, the value is sent as an Optional.

Collectors.maxBy()/minBy() methods are terminal operationsClick to Read Tutorial explaining intermediate & terminal Stream operations.

Internally, these collectors delegate their tasks to the 'reducing collector' with the calls reducing(BinaryOperator. maxBy(comparator)) and reducing(BinaryOperator. minBy(comparator)) for maximum/minimum element determination respectively. (Note that these calls to the reducing collector are internal to the implementation of the Collectors class, and need not be a point of consideration for the end programmer using these methods.) Java 8 code example showing Collectors.maxBy(),minBy() methods' usage
Java 8 code example showing Collectors.maxBy()/minBy() usage
package com.javabrahman.java8.collector;

import com.javabrahman.java8.Employee;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

class MaxMinWithCollectors {
  static List<Employee> employeeList
        = Arrays.asList(new Employee("Tom Jones", 45, 15000.00),
          new Employee("Tom Jones", 45, 7000.00),
          new Employee("Ethan Hardy", 65, 8000.00),
          new Employee("Nancy Smith", 22, 10000.00),
          new Employee("Deborah Sprightly", 29, 9000.00));

  public static void main(String[] args) {
    Optional<Employee> maxSalaryEmp = 
            employeeList.stream()
            .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary)));
    System.out.println("Employee with max salary:" 
            + (maxSalaryEmp.isPresent()? maxSalaryEmp.get():"Not Applicable"));
    Optional<Employee> minAgeEmp =    
            employeeList.stream()
            .collect(Collectors.minBy(Comparator.comparing(Employee::getAge)));
    System.out.println("Employee with min age:"
            + (minAgeEmp.isPresent()? minAgeEmp.get():"Not Applicable"));
  }
}
//Employee.java(POJO Class)
package com.javabrahman.java8;
import java.text.DecimalFormat;
public class Employee {
  private String name;
  private Integer age;
  private Double salary;

  public Employee(String name, Integer age, Double salary) {
    this.name = name;
    this.age = age;
    this.salary = salary;
  }

  //Getters and Setters of name, age & salary go here

  public String toString(){
    DecimalFormat dformat = new DecimalFormat(".##");
    return "Employee Name:"+this.name
        + "  Age:"+this.age
        + "  Salary:"+dformat.format(this.salary);
  }
  //Standard equals() and hashcode() implementations go here
}
 OUTPUT of the above code
Employee with max salary:  Employee Name:Tom Jones    Age:45    Salary:15000.0
Employee with min age:  Employee Name:Nancy Smith    Age:22    Salary:10000.0
Explanation of the code
  • Employee.java is the POJO class i.e. the type of which the Stream is created. It contains 3 attributes - name, age and salary.
  • MaxMinWithCollectors class contains a static list of 5 Employee objects named employeeList.
  • In the main() method of MaxMinWithCollectors class a stream of Employee objects is created by invoking employeeList.stream(). This stream is then terminated by invoking the collect() method with the Collector returned by the Collectors.maxBy() method passed as a parameter.
  • Collectors.maxBy() method needs as its input parameter a Comparator instance. This Comparator instance is created using the Comparator.comparing()Click to Read Tutorial explaining Java 8 Comparators method with the sort key specified using the method referenceClick to Read Tutorial on Java 8 Method References to the getter of salary attribute of Employee class i.e. “Employee::getSalary”.
  • As expected, the Collector returned by Collectors.maxBy() method finds the Employee with the maximum salary and prints it as “Employee Name:Tom Jones Age:45 Salary:15000.0”. Employee details are printed as per the formatting defined in the overridden toString() method in the Employee POJO class. As the maximum value is inside an Optional, the code uses a ternary operator to first determine whether the value is present and then retrieves it. Else it returns the String “Not Applicable”.
  • Similar to the maximum salary determination, employee with minimum age is identified by first creating a stream of Employee objects using employeeList.stream() method. Stream.collect() method is pipelinedClick to Read Tutorial explaining concept of Pipelines in Computing to the stream() method, with Collectors.minBy() as parameter. Collectors.minBy() in turn takes a Comparator instance defined using Comparator.comparing() method with getter to Employee’s age attribute passed as sort key to it via the method reference “Employee::getAge”. A ternary operator is used to check for presence of Optional minimum value before retrieving it.
  • As expected, the employee with minimum age is determined and printed as “Nancy Smith Age:22 Salary:10000.0”.
Summary In this tutorial we understood the working of the predefined Collector returned by the Collectors.maxBy() and Collectors.maxBy() methods. We looked at the formal definition of the Collectors.maxBy()/Collectors.minBy() methods, had a brief look at what actually happens inside the method, and finally saw the Collectors.maxBy()/Collectors.minBy() methods in action with a Java 8 code example followed by its explanation.