简体   繁体   English

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

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

If I want to total a list of accounts' current balances, I can do: 如果我想要列出账户的当前余额列表,我可以这样做:

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

But this expression will return 0, even if all the balances are null. 但是这个表达式将返回0,即使所有余额都为空。 I would like it to return null if all the balances are null, 0 if there are non-null 0 balances, and the sum of the balances otherwise. 如果所有余额都为空,我希望它返回null,如果有非空0余额则返回0,否则返回余额之和。

How can I do this with a lambda expression? 我怎么能用lambda表达式做到这一点?

Many thanks 非常感谢

Once you filtered them from the stream, there's no way to know if all the balances were null (unless check what count() returns but then you won't be able to use the stream since it's a terminal operation). 一旦你从流中过滤它们,就无法知道所有余额是否为null (除非检查count()返回但是你将无法使用流,因为它是一个终端操作)。

Doing two passes over the data is probably the straight-forward solution, and I would probably go with that first: 对数据进行两次传递可能是直接的解决方案,我可能会首先使用它:

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

You could get rid of the filtering step with your solution with reduce , although the readability maybe not be the best: 尽管可读性可能不是最好的,但您可以使用reduce解决方案摆脱过滤步骤:

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

Notice the Long.valueOf call. 注意Long.valueOf调用。 It's to avoid that the type of the conditional expression is long , and hence a NPE on some edge cases. 这是为了避免条件表达式的类型很long ,因此在某些边缘情况下是NPE。


Another solution would be to use the Optional API. 另一种解决方案是使用Optional API。 First, create a Stream<Optional<Long>> from the balances' values and reduce them: 首先,从余额的值创建一个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); 

This will give you an Optional<Long> that will be empty if all the values were null , otherwise it'll give you the sum of the non-null values. 如果所有值都为null ,这将为您提供一个Optional<Long> ,否则它将为您提供非空值的总和。

Or you might want to create a custom collector for this: 或者您可能想为此创建一个自定义收集器:

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

and then: 然后:

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


As you can see, there are various ways to achieve this, so my advice would be: choose the most readable first. 正如您所看到的,有多种方法可以实现这一点,因此我的建议是:首先选择最易读的。 If performance problems arise with your solution, check if it could be improved (by either turning the stream in parallel or using another alternative). 如果您的解决方案出现性能问题,请检查是否可以改进(通过并行转换流或使用其他替代方案)。 But measure, don't guess. 但是衡量,不要猜测。

For now, I'm going with this. 现在,我正在接受这个。 Thoughts? 思考?

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

Because I've filtered nulls already, I'm guaranteed not to hit any. 因为我已经过滤了空值,所以我保证不会打击任何空值。 By making the initial param to reduce 'null', I can ensure that I get null back on an empty list. 通过使初始参数减少'null',我可以确保在空列表中返回null。

Feels a bit hard/confusing to read though. 虽然感觉有点困难/混乱。 Would like a nicer solution.. 想要一个更好的解决方案..

EDIT Thanks to pbabcdefp, I've gone with this rather more respectable solution: 编辑感谢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());

You're trying to do two fundamentally contradicting things: filter out null elements (which is a local operation, based on a single element) and detect when all elements are null (which is a global operation, based on the entire list). 你试图做两个根本矛盾的事情:过滤掉空元素(这是一个基于单个元素的本地操作)并检测所有元素何时为空(这是一个全局操作,基于整个列表)。 Normally you should do these as two separate operations, that makes things a lot more readable. 通常你应该将它们作为两个单独的操作来执行,这使得事情更具可读性。

Apart from the reduce() trick you've already found, you can also resort to underhand tricks, if you know that balance can never be negative for example, you can do something like 除了你已经找到的reduce()技巧之外,你还可以采用不足的技巧,如果你知道平衡永远不会是负面的,例如,你可以做类似的事情

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

But you've got to ask yourself: is what you gain by only iterating across your collection once worth the cost of having written a piece of unreadable and fairly brittle code? 但是你必须问自己:只有在编写了一段难以理解且相当脆弱的代码后才能在您的集合中进行迭代才能获得什么? In most cases the answer will be: no. 在大多数情况下,答案是:不。

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

相关问题 Java 8 Lambda空列表进行空检查 - Java 8 lambda null list to empty check Java 8中的列表为空时如何返回null? - How to return null when list is empty in Java 8? 用于检查 null 并在 java 中的集合/列表为空时将大小返回为 0 的实用程序 - Utility to check null and return size as 0 if empty for a collection/list in java Java 返回一个空列表 - Java return an empty list 如果给定索引处的子列表不是 null,则返回 true,否则返回 false - Return true if sub-list at given index is NOT null, false otherwise 如果空列表返回错误,如果没有列表 Java 8 lambda - if empty list returns error, if not list Java 8 lambda Java 8 中的空和空列表检查 - Null and Empty List Check in Java 8 什么是“可选”的惯用“功能”方式 <String> 并返回默认字符串(如果为null或为空),否则返回修改后的字符串? - What is the idiomatic “functional” way to take an Optional<String> and return a default string if null or empty, a modified string otherwise? 将json嵌套到Java对象列表 <Object> 返回期望值,但列表 <DefinedObject> 返回空值 - nested json to java objects List<Object> returns the expected values but List<DefinedObject> return null values 取消匹配后返回空列表和空列表 - Return null and empty List after unmarsal
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM