【问题标题】:Null check to an list before apply java steam to it在将 java 流应用到列表之前对列表进行空检查
【发布时间】:2021-02-24 08:30:24
【问题描述】:

我有以下代码

item.getSubCategory().stream().map(category -> {
              return subCategoriesList.add(new SubCategoryViewModel(
                      category.get(String.format("_%s",categoryProperties[0])).toString(),
                      category.get(categoryProperties[2]).toString(),
                      category.get(categoryProperties[3]).toString()));
            }).collect(Collectors.toList());

item.getSubCategory() 是一个类别模型列表。现在,如果 getSubCategory 上出现 null,我们会得到一个空指针异常,并且不能对其应用 Steam。在将流应用到列表之前,是否有更好的方法来处理对列表的空检查。

我不想使用IF 语句来检查getSubCategory 上的空值。 java steam API有什么更好的方法吗?

【问题讨论】:

  • Optional.ofNullable(item.getSubCategory()).orElse(List.of()).stream()...
  • 是:修复 getSubCategory 不返回 null
  • ……顺便说一下,String.format("_%s",categoryProperties[0]) 是一种非常昂贵的有效方式来有效地执行"_" + categoryProperties[0]

标签: java java-8 java-stream


【解决方案1】:

返回列表的方法永远不应该返回null - 这是一种非常糟糕的做法。只需在构造函数中初始化列表,如果没有添加任何对象,则返回一个空列表。这样一来,您就可以在使用 getSubCategory 时消除所有需要的空检查。

可以说你最终得到了一个占用内存的未使用对象。然而,除非你真的有很多 item 对象,否则拥有一些“备用”列表不会伤害任何人。请记住,在大多数情况下,代码可读性是第一位的 - 当您看到实际问题时修复性能。

编辑:按照 Holger 的建议,如果您担心这些空列表的内存成本,您可以使用 Collections.emptyList()。在您的item 中,您保留所有内容,仅在需要时创建列表等。但是,在getSubCategory 内部而不是返回null,您应该检查列表是否已初始化,如果未初始化,则返回Collections.emptyList() 代替null。根据文档,Collections.emptyList() 返回一个不可变的列表实例,多次调用将返回同一个实例 - 只会创建一个。

【讨论】:

  • 即使您有很多item 对象并且担心空列表的成本,使用返回单个共享对象的Collections.emptyList() 也可以解决这个问题。但在 Java 8 及更高版本中,使用默认构造函数创建的空 ArrayList 也非常便宜。
  • @Holger 感谢您的评论,并将其添加到我的回答中。
  • @Holger 只是好奇,为什么从 Java 8 开始就很便宜?
  • @Lino 因为从 Java 8 开始,在您实际将元素存储到其中之前,它不会分配数组。所以初始状态是一个具有三个字段的轻量级对象。也许,这种优化已经在 J​​ava 7 中了,我不确定。
  • @Holger 我明白了,我认为逃逸分析发生了一些变化,我什至没有考虑过这个简单的惰性分配。谢谢
【解决方案2】:

从 Java 9 开始,你也可以使用Objects.requireNonNullElse()

Objects.requireNonNullElse(item.getSubCategory(), Collections.emptyList())
    .stream()
    .map(...);

这可能类似于:

Optional.ofNullable(item.getSubCategory()).orElse(Collections.emptyList())
    .stream()
    .map(...);

但方式不同,requireNonNullElse() 将直接返回非空List,而Optional 变体将初始List 包装在Optional 中,然后用orElse() 展开

【讨论】:

    【解决方案3】:

    也许这应该是一个很好的评论候选者,但我怀疑一个足以解释我的观点。


    我经常看到有人建议使用空列表(或任何空集合)而不是 null。在我看来,这是两种完全不同的结果类型:empty listabsent list。也许有时一个空列表就可以了,但并非在所有情况下都如此。

    我同意返回 null 是一种糟糕的做法,会导致许多进一步的问题。但是null 并不是表达没有结果的唯一方式。 我宁愿使用 Optional.empty() 而不是空列表作为默认结果。

    这是一个证明我的观点的例子。

    假设我们有一个从电表获取消耗记录列表的方法。有时设备可能处于离线状态,因此您无法获取任何内容,稍后将重试。 现在看看这两种方法:

    List<ConsumptionAmount> getConsumptionData(Date from, Date to); 
    
    Optional<List<ConsumptionAmount>> getConsumptionData(Date from, Date to); 
    

    第一个实现非常棘手。返回空列表非常令人困惑(因此说根本没有能源消耗)。

    List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
    // report 'zero consumption' to the billing service
    

    另一种选择是传播异常,这也可能导致代码中出现try-catch 混乱(调用者必须了解异常类型并做出相应的行为,否则异常会进一步传播,导致更大的混乱) :

    try {
        List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
        // report consumption to the billing service
    } catch (WhateverException ex) {
        // handle missing data here
    }
    

    相比之下,第二个实现没有提供有关导致数据缺失的问题的任何细节,但它也不会混淆调用者。

    getConsumptionData(from, to).ifPresent( list -> /* report consumption here */);
    

    如果您确实关心丢失的结果,那么只需提供另一种方法参考:

    getConsumptionData(from, to).ifPresentOrElse( 
        list -> /* report consumption here    */,
        ()   -> /* handle missing data here */
    );
    

    对于此类用例(EitherTry),还有更有趣的功能方法,但不幸的是,Java 并没有提供开箱即用的方法。您可以检查第三方框架,例如this one

    【讨论】:

    • 好点,尽管这仅适用于必须区分空列表和缺失列表的情况。许多用例只想获取元素的List,然后遍历所述列表以对每个元素应用操作。在这种情况下,最好返回一个空的List,而不是一个表明List 不存在的值
    • 以您的电表示例和getConsumptionData 方法为例。我认为您正在建立很多前提,即缺少值意味着仪表处于离线状态。但是返回nullOptional.empty 本身没有任何意义,只是没有任何价值。也许设备离线,但也可能意味着代码中有错误,错误返回了null
    • 您已经指定了我更喜欢的两种解决方案之一。如果设备离线,请使用例外。无法访问该设备,这很容易表达,但有一个例外。当然,它看起来不像使用Optional 那样花哨,但它更有意义并且更少依赖于好的文档。我的另一个首选解决方案是将整个响应包装在另一个名为 ElectricityMeterResponse 的对象中,该对象可能具有布尔值 online 和其他方法,例如 ifOnline(Consumer&lt;List&gt; data)。当然,设计一个额外的类需要更多的努力。
    • @magicmn "...不太依赖良好的文档..." ,好吧,在我看来,Optional 对文档的依赖程度低于异常(尤其是运行时的)。您可以通过类型系统表达不存在值的可能性,并在编译时执行安全检查。
    • @magicmn 至于你提出的ElectricityMeterResponse 解决方案,其实很好。我不能说这与返回 Optional 或更确切地说是 Either&lt;YourException, YourResult&gt; 有很大不同。但核心思想是返回一个空列表可能不足以满足要求。因此,您的解决方案比普通的空列表要好得多。
    【解决方案4】:

    之前有没有更好的方法来处理对列表的空检查 对其应用流。

    我只能想到一种方法。使项目 item.getSubCategory() 返回 Optional&lt;YourObject&gt; 而不是实际的对象。

    我不想使用 IF 语句在 getSubCategory 上检查 null。是 java steam API有什么更好的方法吗?

    您的担忧与流 API 无关。你问的是如何处理一个可能为空的对象,这样我就不必使用 If,else 检查。

    【讨论】:

    • 在大多数情况下,不应该使用列表的可选项,而是“空”/“不存在”返回值应该只是一个空列表。
    • 既然你总是可以返回一个空的List&lt;Object&gt;,为什么还要返回Optional&lt;List&lt;Object&gt;&gt;?退回您可以使用的东西总是更好。如果您无法返回可以使用的内容,请返回 Optionalnull
    • @magicmn 因为空列表和没有列表是两个完全不同的结果。
    • @ETO 是的,当然,但首先不应该缺少列表。您可以有一个空列表,但缺少列表是不好的做法...
    • @magicmn 请在下面查看我的答案。评论似乎不足以解释我的解释。
    猜你喜欢
    • 1970-01-01
    • 2020-03-04
    • 2011-09-15
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    相关资源
    最近更新 更多