是否可以只迭代一次流并执行2次或更多次操作?

时间:2015-07-17 16:48:14

标签: functional-programming java-8 reduce

给出代码

List<Integer> numbers = Arrays.asList(2, 4, 3);

int sumTotal = numbers.stream().reduce(-3, (x, y) -> x + y + 3);
int multiplyTotal = numbers.stream().reduce(1, (x, y) -> x * y);

是否可以在仅迭代流一次时执行两个操作?

另外,请注意每个reduce都有不同的标识:-3和1。

3 个答案:

答案 0 :(得分:4)

您可以使用我在this answer中编写的配对收集器:

int[] result = numbers.stream().collect(
        pairing(
                Collectors.reducing(0, (x, y) -> x + y + 3),
                Collectors.reducing(1, (x, y) -> x * y),
                (sum, prod) -> new int[] { sum, prod }));

当然,您可以以任何其他方式组合结果。把它们放到数组只是一个例子。

此收藏夹随时可在我的StreamEx库(MoreCollectors.pairing)中找到。或者你可以使用jOOL库:它有Tuple.collectors方法,它的工作方式类似。

答案 1 :(得分:3)

您可以创建自定义类,并使用可变缩减:

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(2, 4, 3);

    Result result = numbers.stream().collect(Result::new, Result::consume, Result::combine);
    System.out.println("result = " + result);
}

private static class Result {
    private int sum = 0;
    private int product = 1;

    public void consume(int i) {
        sum += i + 3;
        product *= i;
    }

    public void combine(Result r) {
        // READ note below
        sum += r.sum + 3;
        product *= r.product;
    }

    @Override
    public String toString() {
        return "Result{" +
            "sum=" + sum +
            ", product=" + product +
            '}';
    }
}

但是我怀疑你做这件事会因为你正在进行两次迭代而获得更多。

编辑:

请注意,上面的combine()方法在使用并行流时调用,将产生与顺序流不一致的结果。正如Holger在评论中提到的那样,问题是由于操作不遵守减少的身份前提条件这一事实:应用减少任何值V的身份应该产生V,但它产生V + 3以0为身份。

确保不使用并行流来计算结果。

答案 2 :(得分:1)

在JS中,一种解决方案是迭代对象而不是数字。例如:

x

请注意,在您的示例中,y是“备忘录”(或累加器),Fragment是当前值。

同样的想法也应该适用于Java。