【问题标题】:Filtering List with nullable values过滤具有可为空值的列表
【发布时间】:2014-08-12 16:02:23
【问题描述】:

我试图用 java lambda 表达式替换现有循环。

我有一个简单的数据结构,如下所示:

class DbObject{
    private Long objectId;

    private Long basedOnObjectId; //<- this field can be null

    public Long getObjectId() {
        return objectId;
    }

    public void setObjectId(Long objectId) {
        this.objectId = objectId;
    }

    public Long getBasedOnObjectId() {
        return basedOnObjectId;
    }

    public void setBasedOnObjectId(Long basedOnObjectId) {
        this.basedOnObjectId = basedOnObjectId;
    }
}

然后我有一个 DbObjects 列表

我试图用指定的 basedOnObjectId 过滤每个 DbObject:

(1) list.stream().filter(object -> obejct.getBasedOnObjectId()
             .equals(basedOnObject.getObjectId()))
             .collect(Collector.of(List))

当然,这段代码给了我 NullPointer,因为一些 od DbObject 没有 BasedOnObjectId,因为它们是根。

所以很自然的事情是替换字段“Long basedOnObjectId;”可选,但正如我在开始时提到的那样,存在现有的生产代码,它不是快速修复。

所以我试图做出一些改变,我评估以下句子:

 Optional<Long> option = Optional.ofNullable(objectWithNullBasedOnId.getBasedOnObjectId());

并尝试使用 .get() 方法或 .ifPresent() 但 .get() 也会抛出空指针异常,并且 ifPresent 不专用于流。

我注意到这一点,当我拥有带有 null basedOnObjectId 的 DbObject 时,我不必检查值是否完全为 null,而只需跳过这一步执行。

所以我决定使用 .orElse() 方法并返回一些假值。在这种情况下,我返回 0L 值,因为该索引在 DB 中不存在 :)

所以我的代码:

 (2) List newList = list.stream().filter(object -> Optional.ofNullable(object.getBasedOnObjectId())
                             .orElse(0L)
                             .equals(basedOnObject.getObjectId()) )
                             .collect(Collectors.toList()) ;

在这种情况下,这可以正常工作,但我认为这是某种解决方法。在其他语言中,可以使用 null 值跳过执行,或者使用类似 skip-skipWhen 的方法。

我的问题是:是否有可能使用 java 8 中的专用方法(例如可选)实现 (2) 表达式的目标并编写“流式”:)

【问题讨论】:

  • 你让它变得比它需要的更复杂。过滤掉空值:list.stream().filter(o -> o.basedOnObjectId() != null)...
  • 或者,只使用 Objects.equal,它会为您处理 null。

标签: java lambda java-8 java-stream


【解决方案1】:

这里不需要介绍Optional。您只需要知道 null 在您的域中,并相应地进行处理。像这样:

list.stream().filter(o -> Objects.equals(o.getBasedOnObjectId(), 
                                         basedOnObject.getObjectId()))
             .collect(toList());

或通过显式过滤掉空值 (.filter(o -&gt; o.getBasedOnObjectId() != null) 或将空值检查滚动到您现有的过滤条件中。

没有理由让这变得比它需要的更复杂。 Objects.equals() 可能是你想要的,就用它吧。

【讨论】:

    【解决方案2】:

    如果您比较 Optional&lt;Long&gt; 而不是 Long,则无需区分 presentabsent 对象。

    Optional<Long> root = Optional.of(basedOnObject.getObjectId());
    list.stream()
        .map(DbObject::getBasedOnObjectId)
        .map(Optional::ofNullable)
        .filter(root::equals)
        .collect(Collectors.toList());
    

    在这种情况下,它更简单,因为您可以反转比较:

    list.stream()
        .filter(o -> basedOnObject.getObjectId().equals(o.getBasedOnObjectId()))
        .collect(Collectors.toList());
    

    【讨论】:

    • 谢谢,我错过了可以反转比较的事实 :) 但在其他情况下 .map(Optional::ofNullable) 可能是很好的解决方案
    【解决方案3】:

    您可能希望在将 null 对象传递给后续操作之前从流中实际删除它们。尝试将对象映射到 BasedOnObjectId 表单,然后过滤空值:

    list.stream()
        .map(object -> object.getBasedOnObjectId())
        .filter(object -> object != null)
        ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多