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

use*_*842 9 java lambda

如果我想要列出账户的当前余额列表,我可以这样做:

accountOverview.setCurrentBalance(account.stream().
                filter(a -> a.getCurrentBalance() != null).
                mapToLong(a -> a.getCurrentBalance()).
                sum());
Run Code Online (Sandbox Code Playgroud)

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

我怎么能用lambda表达式做到这一点?

非常感谢

Ale*_* C. 6

一旦你从流中过滤它们,就无法知道所有余额是否都是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();
Run Code Online (Sandbox Code Playgroud)

您可以使用您的解决方案摆脱过滤步骤reduce,尽管可读性可能不是最好的:

Long sum = account.stream()
                  .reduce(null, (l1, l2) -> l1 == null ? l2 :
                                                         l2 == null ? l1 : Long.valueOf(l1 + l2));
Run Code Online (Sandbox Code Playgroud)

注意这个Long.valueOf电话.这是为了避免条件表达式的类型long,因此在某些边缘情况下是NPE.


另一种解决方案是使用OptionalAPI.首先,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);
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

OptionalLong opt = account.stream().map(Account::getBalance).collect(SumIntoOptional::new, SumIntoOptional::add, SumIntoOptional::merge).getSum();
Run Code Online (Sandbox Code Playgroud)


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


use*_*842 4

现在,我就这样吧。想法?

        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; } }));
Run Code Online (Sandbox Code Playgroud)

因为我已经过滤了空值,所以我保证不会碰到任何空值。通过使初始参数减少“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());
Run Code Online (Sandbox Code Playgroud)