[英]Java streams: one liner to perform multiple numeric operations on Int stream
[英]Adding multiple fields in Java streams (and conditional stream operations)
假设我有这个课程:
public class Thing {
private BigDecimal field1;
private BigDecimal field2;
private BigDecimal otherField1;
private BigDecimal otherField2;
private BigDecimal otherField3;
}
并且在另一个类中,我在每个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()
事情没有成功。
使用带有自定义收集器帮助程序的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);
}
}
由于您要统一处理字段,您可以考虑使用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);
}
在第一种方法中,您希望将所有field1
和field2
在一起,忽略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);
}
第二个示例也是如此,在这种情况下,您希望将otherField1
和otherField2
或otherField3
之间的差异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);
}
解决此任务的另一种等效方法是使用map()
:在这种情况下,您可以将每个元素映射到您想要求和的值,并通过对所有BigInteger
求和来减少Stream。 例如,在第一个例子中,你将每个地图Thing
进入的总和field1
和field2
,考虑到其潜在的null
-ness。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.