一旦您从流中过滤它们,就无法知道是否所有余额都是 null(除非检查 count() 返回的内容,但您将无法使用流,因为它是终端操作) .
对数据进行两次传递可能是直接的解决方案,我可能会先这样做:
boolean allNulls = account.stream().map(Account::getBalance).allMatch(Objects::isNull);
Long sum = allNulls ? null : account.stream().map(Account::getBalance).filter(Objects::nonNull).mapToLong(l -> l).sum();
您可以使用 reduce 的解决方案摆脱过滤步骤,尽管可读性可能不是最好的:
Long sum = account.stream()
.reduce(null, (l1, l2) -> l1 == null ? l2 :
l2 == null ? l1 : Long.valueOf(l1 + l2));
注意Long.valueOf 电话。这是为了避免条件表达式的类型是long,因此在某些极端情况下会出现 NPE。
另一种解决方案是使用
Optional API。首先,从余额的值创建一个
Stream<Optional<Long>> 并减少它们:
Optional<Long> opt = account.stream()
.map(Account::getBalance)
.flatMap(l -> Stream.of(Optional.ofNullable(l)))
.reduce(Optional.empty(),
(o1, o2) -> o1.isPresent() ? o1.map(l -> l + o2.orElse(0L)) : o2);
这会给你一个Optional<Long>,如果所有值都是null,它将为空,否则它会给你非空值的总和。
或者您可能想为此创建一个自定义收集器:
class SumIntoOptional {
private boolean allNull = true;
private long sum = 0L;
public SumIntoOptional() {}
public void add(Long value) {
if(value != null) {
allNull = false;
sum += value;
}
}
public void merge(SumIntoOptional other) {
if(!other.allNull) {
allNull = false;
sum += other.sum;
}
}
public OptionalLong getSum() {
return allNull ? OptionalLong.empty() : OptionalLong.of(sum);
}
}
然后:
OptionalLong opt = account.stream().map(Account::getBalance).collect(SumIntoOptional::new, SumIntoOptional::add, SumIntoOptional::merge).getSum();
如您所见,有多种方法可以实现这一点,所以我的建议是:首先选择最易读的。如果您的解决方案出现性能问题,请检查是否可以改进(通过并行处理流或使用其他替代方案)。但是衡量,不要猜测。