3.4 利用reduce方法校验排序

问题

用户希望检查排序是否正确。

方案

使用 reduce 方法检查每对元素。

讨论

java.util.stream.Stream 接口定义的 reduce 方法传入 BinaryOperator 作为参数:

  1. Optional<T> reduce(BinaryOperator<T> accumulator)

BinaryOperator 是一种输入类型和输出类型相同的 Function。根据范例 3.3 的讨论,BinaryOperator 的第一个元素通常为累加器,第二个元素传入流的每个值,如例 3-29 所示。

例 3-29 利用 reduce 方法对 BigDecimal 求和

  1. BigDecimal total = Stream.iterate(BigDecimal.ONE, n -> n.add(BigDecimal.ONE))
  2. .limit(10)
  3. .reduce(BigDecimal.ZERO, (acc, val) -> acc.add(val));
  4. System.out.println("The total is " + total);

➊ 使用 BigDecimal 类定义的 add 方法作为 BinaryOperator

与之前一样,lambda 表达式返回的任何值都将作为下一次迭代时变量 acc 的值。在本例中,程序计算前 10 个 BigDecimal 实例的值。

这是 reduce 方法最典型的应用方式。虽然 acc 在本例中用作累加器,但并不意味着它必须作为累加器使用。接下来,我们采用范例 4.1 讨论的方法来排序字符串。例 3-30 展示的代码段根据字符串的长度对它们进行排序。

例 3-30 根据长度对字符串排序

  1. List<String> strings = Arrays.asList(
  2. "this", "is", "a", "list", "of", "strings");
  3. List<String> sorted = strings.stream()
  4. .sorted(Comparator.comparingInt(String::length))
  5. .collect(toList());

➊ 结果为 ["a", "is", "of", "this", "list", "strings"]

那么,如何验证排序是否正确呢?答案是比较每对相邻的字符串,确保第一个字符串的长度不大于第二个字符串。reduce 方法就能实现这个功能,如例 3-31 所示(Junit 测试用例的一部分)。

例 3-31 测试字符串排序是否正确

  1. strings.stream()
  2. .reduce((prev, curr) -> {
  3. assertTrue(prev.length() <= curr.length());
  4. return curr;
  5. });

❶ 检查每对字符串的排序是否正确

curr 成为 prev 的下一个值

对于每对连续的字符串,程序将前一个参数和当前参数分别赋给变量 prevcurrassertTrue 用于测试前一个字符串的长度是否小于或等于当前字符串的长度。需要注意的是,reduce 方法的参数将返回当前字符串 curr 的值,它在下一次迭代时成为 prev 的值。

为使上述代码能正确执行,唯一的要求是采用顺序(sequential)流或有序流。

另见

有关 reduce 方法的讨论请参见范例 3.3,有关排序的讨论请参见范例 4.1。