【问题标题】:Java lambda to return null if empty list otherwise sum of values?Java lambda如果为空列表则返回null,否则为值总和?
【发布时间】:2015-08-03 10:30:34
【问题描述】:

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

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

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

如何使用 lambda 表达式做到这一点?

非常感谢

【问题讨论】:

  • sum() 返回原始整数。它不能为空。
  • 这就是问题所在。答案是什么? ;-)
  • 最好把它分成两行,一个过滤掉nulls,另一个返回null,如果大小是0,否则求和。可以使用reduce 而不是filter 在一行中完成,但可读性会受到影响。
  • 实际上返回 null 是不好的做法(参见 NullObject 模式)...
  • 不,我的意思是把它变成一个数组或列表来检查大小。

标签: java lambda


【解决方案1】:

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

这会给你一个Optional&lt;Long&gt;,如果所有值都是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());
    

    【讨论】:

      【解决方案3】:

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

      除了你已经发现的reduce()技巧之外,你还可以使用不正当的技巧,例如,如果你知道余额永远不会是负数,你可以这样做

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

      但是您必须问自己:仅通过一次迭代您的集合所获得的收益是否值得编写一段不可读且相当脆弱的代码的成本?在大多数情况下,答案是:不。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-09
        • 2016-10-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多