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

2022-09-04 02:49:37

如果我想合计帐户的当前余额列表,我可以执行以下操作:

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

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

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

非常感谢


答案 1

一旦你从流中过滤了它们,就无法知道是否所有的余额都是(除非检查返回的内容,但这样你将无法使用流,因为它是一个终端操作)。nullcount()

对数据进行两次传递可能是直接的解决方案,我可能会先这样做:

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

请注意该调用。这是为了避免条件表达式的类型是 ,因此在某些边缘情况下是 NPE。Long.valueOflong


另一种解决方案是使用API。首先,从余额的值创建一个并减少它们:OptionalStream<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();


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

答案 2

现在,我要用这个。思潮?

        accountOverview.setCurrentBalance(account.stream().
                filter(a -> a.getCurrentBalance() != null).
                map(a -> a.getCurrentBalance()).
                reduce(null, (i,j) -> { if (i == null) { return j; } else { return i+j; } }));

因为我已经过滤了空值,所以我保证不会命中任何空值。通过创建初始参数来减少“null”,我可以确保在空列表中返回null。

虽然感觉有点困难/令人困惑。想要一个更好的解决方案。

编辑 感谢pbabcdefp,我采用了这个更受人尊敬的解决方案:

        List<Account> filtered = account.stream().
                filter(a -> a.getCurrentBalance() != null).
                collect(Collectors.toList());

        accountOverview.setCurrentBalance(filtered.size() == 0?null:
            filtered.stream().mapToLong(a -> a.getCurrentBalance()).
            sum());

推荐