繁体   English   中英

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

[英]Java lambda to return null if empty list otherwise sum of values?

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

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

如果所有值都为null ,这将为您提供一个Optional<Long> ,否则它将为您提供非空值的总和。

或者您可能想为此创建一个自定义收集器:

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

然后:

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


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

现在,我正在接受这个。 思考?

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

你试图做两个根本矛盾的事情:过滤掉空元素(这是一个基于单个元素的本地操作)并检测所有元素何时为空(这是一个全局操作,基于整个列表)。 通常你应该将它们作为两个单独的操作来执行,这使得事情更具可读性。

除了你已经找到的reduce()技巧之外,你还可以采用不足的技巧,如果你知道平衡永远不会是负面的,例如,你可以做类似的事情

 long sum = account.stream().
                 mapToLong(a -> a.getCurrentBalance() == null ? 0 : a.getCurrentBalance()+1).
                 sum() - account.size();
 Long nullableSum = sum < 0 ? null : sum;

但是你必须问自己:只有在编写了一段难以理解且相当脆弱的代码后才能在您的集合中进行迭代才能获得什么? 在大多数情况下,答案是:不。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM