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

时间:2016-04-07 19:21:02

标签: java lambda functional-programming java-8 java-stream

假设我有这门课程:

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

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

并且在另一个类I-for-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 个答案:

答案 0 :(得分:5)

使用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);
    }

}

答案 1 :(得分:3)

由于您要统一处理字段,因此您可以考虑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);
}

答案 2 :(得分:1)

在第一种方法中,您希望将所有field1field2加在一起,忽略null。您可以使用单个Stream管道执行此操作,如您所暗示的那样,使用3参数reduce方法。

在这种情况下,身份仍为BigDecimal.ZERO。如果每个字段不为空,则累加器函数会将当前累加的结果添加到每个字段。最后,仅用于并行处理的组合器添加了两个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);
}

您的第二个示例也是如此,在这种情况下,您希望将otherField1otherField2otherField3之间的差异相加,具体取决于它们是否为{{1 }}:

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映射到Thingfield1的总和,同时考虑到它们的潜在field2 -