如果列表为空,Java lambda 返回 null,否则返回值的总和?

标签 java lambda

如果我想汇总一个账户的当前余额列表,我可以这样做:

accountOverview.setCurrentBalance(account.stream().
                filter(a -> a.getCurrentBalance() != null).
                mapToLong(a -> a.getCurrentBalance()).
                sum());

但是这个表达式将返回 0,即使所有余额都为空。如果所有余额都为空,我希望它返回 null,如果有非空 0 余额,则返回 0,否则返回余额之和。

如何使用 lambda 表达式执行此操作?

非常感谢

最佳答案

一旦从流中过滤掉它们,就无法知道是否所有余额都是 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应用程序接口(interface)。首先,创建一个 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();


如您所见,有多种方法可以实现这一点,所以我的建议是:首先选择最易读的。如果您的解决方案出现性能问题,请检查它是否可以改进(通过并行转换流或使用其他替代方案)。但是测量,不要猜测。

关于如果列表为空,Java lambda 返回 null,否则返回值的总和?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31785082/

相关文章:

c++ - 如何将 boost regex_replace 与 lambda 函数一起使用?

java - uibinder onload 未调用

java - 不同情况下具有枚举值的 ConfigurationProperties

c# - Lambda 表达式,其中列等于列表项

Haskell,创建一个给定特定类型的函数

Java 用 lambda 替换方法

java - 从泛型类返回数组

java - 在不同多核处理器上进行测试的速度标准化

Java 服务器通过 JSON 处理来自 iOS 的请求?

java - 如何使用 Lambda 表达式 .reduce() 方法减少给定列表