在Java流中添加多个字段(以及条件流操作)

8

假设我有这个类:

public class Thing {
    private BigDecimal field1;
    private BigDecimal field2;

    private BigDecimal otherField1;
    private BigDecimal otherField2;
    private BigDecimal otherField3;
}

在另一个类中,我使用 for-each 遍历 List<Thing> things,将 field1 和 field2 添加到总和中,并在迭代完成后返回该总和。

但是我想使用 Java 流来完成这个任务。以下是我的代码 - 它可以工作,但我觉得肯定有一种方法可以将其压缩成一个流:

public BigDecimal addFields(List<Thing> things) {
    BigDecimal field1sum = things.parallelStream()
                                     .filter(thing -> thing.getField1() != null)
                                     .map(Thing::getField1)
                                     .reduce(BigDecimal.ZERO, BigDecimal::add);

     BigDecimal field2sum = things.parallelStream()
                                     .filter(thing -> thing.getField2() != null)
                                     .map(Thing::getField2)
                                     .reduce(BigDecimal.ZERO, BigDecimal::add);
     return field1sum.add(field2sum);
}

我猜测答案是使用 reduce() 方法,它需要三个参数,其中一个是 BiFunction,但我还没有找出如何使它工作。编辑:我认为我可以将 (x,y) -> x.add(y) 传递给 reduce(),但问题变成了如何对这两个字段都进行 map()
此外,是否可能将这段命令式代码转换为函数式流?
public BigDecimal addOtherFields(List<Thing> things) {
    BigDecimal result = BigDecimal.ZERO;
    for (Thing thing : things) {
        if (thing.getOtherField2() != null) {
            BigDecimal otherField2 = thing.getOtherField2();
            otherField2 = thing.getOtherField1().subtract(otherField2);
            result = result.add(otherField2);
         } else if (thing.getOtherField3() != null) {
            BigDecimal otherField3 = thing.getOtherField3();
            otherField3 = thing.getOtherField1.subtract(otherField3);
            result = result.add(otherField3);
         }
     }
     return result;
 }

或者更准确地说,我该如何在基于流的方法中处理条件检查?我试图用 filter() 进行过滤,但未成功。
3个回答

8
使用 collect(),配合自定义的收集器辅助类,就像使用 IntSummaryStatistics 一样。
things.stream()
      .collect(ThingCollectorHelper::new, 
               ThingCollectorHelper::accept,
               ThingCollectorHelper::combine);

您的辅助类将类似于以下内容:

您的辅助类将类似于以下内容:

class ThingCollectorHelper {
    BigDecimal sum1 = BigDecimal.ZERO;
    BigDecimal sum2 = BigDecimal.ZERO;

    void accept(Thing t) {
        if (t.field1 != null)
            sum1 = sum1.plus(t.field1);
        if (t.field2 != null)
            sum2 = sum2.plus(t.field2);
    }

    void combine(ThingCollectorHelper other) {
        sum1 = sum1.plus(other.sum1);
        sum2 = sum2.plus(other.sum2);
    }

}


4

如果你要统一处理这些字段,可以考虑使用flatMap

public BigDecimal addFields(List<Thing> things) {
    return things.parallelStream()
        .flatMap(thing -> Stream.of(thing.getField1(), thing.getField2()))
        .filter(Objects::nonNull)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}
public BigDecimal addOtherFields(List<Thing> things) {
    return things.parallelStream()
        .flatMap(thing ->
            Stream.of(thing.getOtherField2(), thing.getOtherField3())
                .filter(Objects::nonNull)
                .map(thing.getOtherField1()::subtract)
        ).reduce(BigDecimal.ZERO, BigDecimal::add);
}

1
在您的第一种方法中,您希望将所有field1field2相加,忽略null。您可以通过单个流管道实现这一点,如您所示,使用具有3个参数的reduce方法。
在这种情况下,身份仍然是BigDecimal.ZERO。累加器函数将每个字段添加到当前累积结果中,如果它们不为null。最后,组合器(仅用于并行处理)将两个BigDecimal值相加。
public BigDecimal addFields(List<Thing> things) {
    return things.parallelStream().reduce(BigDecimal.ZERO, (a, t) -> {
         if (t.getField1() != null) a = a.add(t.getField1());
         if (t.getField2() != null) a = a.add(t.getField2());
         return a;
     }, BigDecimal::add);
}

对于你的第二个例子,情况也是一样的,在这种情况下,你想要计算 otherField1otherField2 或者 otherField3 之间的差异,并根据它们是否为 null 来进行求和:

public BigDecimal addOtherFields(List<Thing> things) {
    return things.stream().reduce(BigDecimal.ZERO, (a, t) -> {
        if (t.getOtherField2() != null) {
            return a.add(t.getOtherField1().subtract(t.getOtherField2()));
        } else if (t.getOtherField3() != null) {
            return a.add(t.getOtherField1().subtract(t.getOtherField3()));
        }
        return a;
    }, BigDecimal::add);
 }

另一个解决此任务的等效方法是使用 map():在这种情况下,您将每个元素映射到您想要求和的值,并通过对所有 BigInteger 求和来缩小 Stream。例如,在第一个示例中,您将每个 Thing 映射到 field1field2 的总和,考虑它们可能为 null


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接