[英]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。
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();
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.