如何使用 Lambda 表达式 .reduce() 方法减少给定列表


List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6, 8, 9, 10);
integers.stream().filter((integer) -> integer % 2 == 0).collect(Collectors.toList());

如上所示integers是一个列表,我们只需要从中过滤偶数。我可以通过使用.filter()方法来实现。但是,是否有可能用.reduce()方法实现相同的目标。希望,.reduce()方法通过执行给定的 Bynary操作并返回缩减列表来过滤掉所有其他元素。

如果我对.reduce()方法的理解不正确,请让我知道这种方法到底有什么作用。

你对减少的理解是错误的。 reduce将重复对所有元素应用一个函数以获得一个结果

你似乎认为减少就像做

1, 2, 3, 5, 6, 8, 9, 10
│  │  │  │  │  │  │  │
└op┘  └op┘  └op┘  └op┘
  │     │     │     │
    result list

然而,事实上,它确实

1, 2, 3, 5, 6, 8, 9, 10
│  │  │  │  │  │  │  │
└op┘  └op┘  └op┘  └op┘
  │    │      │    │
  └─op─┘      └─op─┘
     │          │
     └────op────┘
           │
   final result value

虽然这是一个概念观点,但未指定确切的操作顺序。顺序执行将类似于(((1 op 2) op 3) op 4)…而并行执行将是上述树中的执行和部分顺序执行的混合。


如果先将每个元素转换为List,然后使用连接每个列表的列表操作,则可以滥用reduce来创建结果列表,但是,这有两个问题:

  • 它没有提供所需的"跳过(原始列表)的每个第二个元素"逻辑;如果你看一下上面的树,应该会很清楚,不可能制定一个正确的op函数,在每个可能的执行场景中都做到这一点。
  • 创建临时列表并将它们连接起来效率非常低

后一点可以通过使用 collect 来解决,这是一种可变的还原,因此,允许您使用可以添加项目的可变列表,但是,它没有解决第一点,包括所需的过滤器会违反合同并且仅在顺序执行中工作。

因此,解决方案是为源列表中的所有元素定义一个filter,然后进行可变还原以使用 collect 创建结果列表,令人惊讶的是,这正是您的原始代码所做的:

… .filter(integer -> integer % 2 == 0).collect(Collectors.toList());

您可以使用 Stream.reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) 方法,该方法采用三个参数:

    标识
  • 标识元素既是缩减的初始值,也是流中没有元素时的默认结果。在您的情况下,它将是一个空列表
  • 累加器:
  • 累加器函数采用两个参数:归约的部分结果和流的下一个元素(在本例中为整数)。它应用对模 2 的检查,然后返回新的部分结果。
  • 合并器
  • :其目的是合并正在并行处理的流批处理的内部临时收集器-累加器。

例如:

BinaryOperator<ArrayList<Integer>> combiner = (x, y) -> { x.addAll(y); return x; };
BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>> accumulator = (x, y) -> {
    if (y % 2 == 0) {
        x.add(y);
    }
    return x;
};
List<Integer> list = Stream.of(1, 2, 3, 5, 6, 8, 9, 10).reduce(new ArrayList<Integer>(),
                                                               accumulator,
                                                               combiner);
System.out.println(list);

请注意,此解决方案可能不适用于并行流。此外,坚持.filter()方法太容易了,所以我强烈建议你这样做。

最新更新