4.7 查找最大值和最小值

问题

用户希望确定流中的最大值或最小值。

方案

既可以使用 BinaryOperator 接口定义的 maxByminBy 方法,也可以使用 Stream 接口定义的 maxmin 方法,还可以使用 Collectors 类定义的 maxByminBy 方法。

讨论

BinaryOperatorjava.util.function 包定义的一种函数式接口,它继承自 BiFunction 接口,适合在函数和返回值的参数属于同一个类时使用。

BinaryOperator 接口包括两种静态方法:

  1. static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator)
  2. static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator)

两种方法根据所提供的 Comparator,返回一个 BinaryOperator

我们以一个 Employee POJO 为例,讨论如何获取流的最大值。如例 4-24 所示,Employee POJO 包括 namesalarydepartment 这三个特性。

例 4-24 Employee POJO

  1. public class Employee {
  2. private String name;
  3. private Integer salary;
  4. private String department;
  5.  
  6. // 其他方法
  7. }
  8.  
  9. List<Employee> employees = Arrays.asList(
  10. new Employee("Cersei", 250_000, "Lannister"),
  11. new Employee("Jamie", 150_000, "Lannister"),
  12. new Employee("Tyrion", 1_000, "Lannister"),
  13. new Employee("Tywin", 1_000_000, "Lannister"),
  14. new Employee("Jon Snow", 75_000, "Stark"),
  15. new Employee("Robb", 120_000, "Stark"),
  16. new Employee("Eddard", 125_000, "Stark"),
  17. new Employee("Sansa", 0, "Stark"),
  18. new Employee("Arya", 1_000, "Stark"));
  19.  
  20. Employee defaultEmployee =
  21. new Employee("A man (or woman) has no name", 0, "Black and White");

❶ 员工集合

❷ 流为空时的默认值

给定一个由员工构成的集合,可以使用 Stream.reduce 方法,传入 BinaryOperator 作为参数。例 4-25 展示了如何查找工资最高的员工信息。

例 4-25 BinaryOperator.maxBy 方法的应用

  1. Optional<Employee> optionalEmp = employees.stream()
  2. .reduce(BinaryOperator.maxBy(Comparator.comparingInt(Employee::getSalary)));
  3. System.out.println("Emp with max salary: " +
  4. optionalEmp.orElse(defaultEmployee));

请注意,reduce 方法需要传入 BinaryOperator 作为参数。静态方法 maxBy 根据所提供的 Comparator 生成该 BinaryOperator,并按工资高低对员工进行比较。

上述方案是可行的,不过采用 Stream.max 方法其实更简单,该方法可以直接应用于流:

  1. Optional<T> max(Comparator<? super T> comparator)

例 4-26 显示了 max 方法的应用。

例 4-26 Stream.max 方法的应用

  1. optionalEmp = employees.stream()
  2. .max(Comparator.comparingInt(Employee::getSalary));

Stream.max 方法与 BinaryOperator.maxBy 方法的结果并无不同。

此外,几种基本类型流(IntStreamLongStreamDoubleStream)也提供一个不传入任何参数的 max 方法,其应用如例 4-27 所示。

例 4-27 查找最高工资

  1. OptionalInt maxSalary = employees.stream()
  2. .mapToInt(Employee::getSalary)
  3. .max();
  4. System.out.println("The max salary is " + maxSalary);

在本例中,mapToInt 方法通过调用 getSalary 方法将员工流转换为整数流,并返回 IntStream。之后,Max 方法返回 OptionalInt

类似地,Collectors 工具类也定义了一种称为 maxBy 的静态方法,可以直接用于查找最高工资,如例 4-28 所示。

例 4-28 Collectors.maxBy 方法的应用

  1. optionalEmp = employees.stream()
  2. .collect(Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)));

但是,Collectors.maxBy 方法不便处理,最好采用 Stream.max 方法作为替代(如例 4-27 所示)。Collectors.maxBy 方法在用作下游收集器(即对分组或分区操作进行后期处理)时相当有用。例 4-29 通过 Collectors.groupingBy 方法创建了一个部门到员工列表的映射,然后计算每个部门中工资最高的员工。

例 4-29 Collectors.maxBy 用作下游收集器

  1. Map<String, Optional<Employee>> map = employees.stream()
  2. .collect(Collectors.groupingBy(
  3. Employee::getDepartment,
  4. Collectors.maxBy(
  5. Comparator.comparingInt(Employee::getSalary))));
  6. map.forEach((house, emp) ->
  7. System.out.println(house + ": " + emp.orElse(defaultEmployee)));

BinaryOperator.minByCollectors.minBy 方法的用法与相应的 maxBy 方法类似。

另见

有关 Function 接口的讨论请参见范例 2.4,有关下游收集器的讨论请参见范例 4.6。