简体   繁体   English

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

[英]Adding multiple fields in Java streams (and conditional stream operations)

Let's say I have this class: 假设我有这个课程:

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

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

and in another class I for-each over a List<Thing> things , adding field1 and field2 to a sum that I return when the iteration is done. 并且在另一个类中,我在每个List<Thing> things ,将field1和field2添加到迭代完成时返回的总和。

But what I want is to accomplish that with Java streams. 但我想要的是用Java流来实现这一目标。 The following is what I have--it works, but I feel like there has to be a way to condense it down to one stream: 以下是我所拥有的 - 它的工作原理,但我觉得必须有一种方法可以将其浓缩为一个流:

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

I suspect the answer is the reduce() method that takes three arguments, one of which is a BiFunction, but I haven't been able to figure out how to make that work. 我怀疑答案是reduce()方法,它接受三个参数,其中一个是BiFunction,但我无法弄清楚如何使其工作。 Edit: I think I could pass in (x,y) -> x.add(y) to reduce() , but then the question becomes how do I map() both of those fields? 编辑:我想我可以传入(x,y) -> x.add(y)reduce() ,但问题是如何map()这两个字段?

Additionally, is it possible/how would I go about turning this imperative code into a functional stream? 另外,是否有可能/如何将此命令式代码转换为功能流?

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;
 }

Or, to be a bit more precise, how would I handle that conditional check in a stream based approach? 或者,为了更精确一点,我将如何处理基于流的方法中的条件检查? I was trying to filter() things out without success. 我试图filter()事情没有成功。

Use collect() , with a custom collector helper, not unlike IntSummaryStatistics . 使用带有自定义收集器帮助程序的collect() ,与IntSummaryStatistics不同。

things.stream()
      .collect(ThingCollectorHelper::new, 
               ThingCollectorHelper::accept,
               ThingCollectorHelper::combine);

Your helper class will be something like: 你的助手类将是这样的:

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

} }

Since you're going to treat the fields uniformly, you may consider flatMap : 由于您要统一处理字段,您可以考虑使用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);
}

In your first method, you want to sum all field1 and field2 together, ignoring null . 在第一种方法中,您希望将所有field1field2在一起,忽略null You can do this with a single Stream pipeline by, as you hinted, the 3 argument reduce method. 您可以使用单个Stream管道执行此操作,如您所暗示的那样,使用3参数reduce方法。

In this case, the identity is still BigDecimal.ZERO . 在这种情况下,标识仍然是BigDecimal.ZERO The accumulator function adds to the current accumulated result each field if they're not null. 如果每个字段不为空,则累加器函数会将当前累加的结果添加到每个字段。 Finally, the combiner, which is only used for parallel processing, adds the two BigDecimal values. 最后,仅用于并行处理的组合器添加了两个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);
}

The same goes for your second example where, in this case, you want to sum the difference between otherField1 and otherField2 or otherField3 depending on whether or not they are null : 第二个示例也是如此,在这种情况下,您希望将otherField1otherField2otherField3之间的差异otherField1 ,具体取决于它们是否为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);
 }

Another equivalent way of solving this task would be to use map() : in that case, you would map each element into the value you want to sum, and reduce the Stream by summing all BigInteger s. 解决此任务的另一种等效方法是使用map() :在这种情况下,您可以将每个元素映射到您想要求和的值,并通过对所有BigInteger求和来减少Stream。 For example, in the first example, you would map each Thing into the sum of field1 and field2 , taking into account their potential null -ness. 例如,在第一个例子中,你将每个地图Thing进入的总和field1field2 ,考虑到其潜在的null -ness。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM