【问题标题】:How should we manage jdk8 stream for null values我们应该如何为空值管理 jdk8 流
【发布时间】:2013-06-13 07:00:44
【问题描述】:

我知道这个主题可能有点in advance,因为 JDK8 还没有发布(而且现在还没有..)但我正在阅读一些关于 Lambda 表达式的文章,特别是与新集合 API 相关的部分称为流。

这是Java Magazine article 中给出的示例(它是一种水獭种群算法..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

我的问题是,如果在 Set 内部迭代过程中,其中一只水獭为空,会发生什么?

我希望抛出 NullPointerException,但也许我仍然停留在以前的开发范式(非功能性)中,有人可以告诉我应该如何处理吗?

如果这真的抛出 NullPointerException,我觉得这个功能很危险,只能按如下方式使用:

  • 开发人员确保没有空值(可能使用以前的 .filter(o -> o != null))
  • 开发人员确保应用程序永远不会 生成 null otter 或特殊的 NullOtter 对象来处理。

什么是最好的选择,或任何其他选择?

【问题讨论】:

  • 我会说程序员在这里做正确的事情; JVM 和编译器只能做这么多。但是请注意,某些集合实现不允许空值。
  • 你可以使用filter(Objects::nonNull)Objects from java.utils

标签: java null java-8 java-stream


【解决方案1】:

虽然答案是 100% 正确的,但还是有一个小建议,可以通过 Optional 改进列表本身的 null 案例处理:

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList) 部分将允许您很好地处理listOfStuff 为 null 的情况并返回一个 emptyList 而不是因 NullPointerException 而失败。

【讨论】:

  • 我喜欢这个,避免显式检查 null。
  • 这看起来最清晰......很好,正是我需要的
  • 它明确地检查 null,只是以不同的方式。如果它允许为空,它应该是一个可选的,这就是想法,对吧?禁止所有带有选项的空值。此外,返回 null 而不是空列表是不好的做法,所以如果我看到这个,它会显示出两种代码味道:没有使用 Optionals,也没有返回空流。而后者已经有 20 多年的历史了,所以已经很成熟了……
  • 如果我想返回一个 null 而不是空列表怎么办?
  • @AshburnRK 这是一个不好的做法。您应该返回一个空列表。
【解决方案2】:

Stuart 的回答提供了很好的解释,但我想再举一个例子。

我在尝试对包含空值的流执行reduce 时遇到了这个问题(实际上是LongStream.average(),这是一种缩减)。由于average() 返回OptionalDouble,我假设Stream 可能包含空值,而是抛出了NullPointerException。这是由于 Stuart's explanation 的 null v.empty。

所以,正如 OP 所建议的,我添加了一个过滤器,如下所示:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

或者正如下面所指出的,使用 Java API 提供的谓词:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

来自 Stuart 链接的邮件列表讨论: Brian Goetz on nulls in Streams

【讨论】:

    【解决方案3】:

    目前的想法似乎是“容忍”空值,即一般允许它们,尽管有些操作的容忍度较低,最终可能会抛出 NPE。请参阅 Lambda 库专家组邮件列表中的 discussion of nulls,特别是 this message。随后出现了围绕选项 #3 的共识(Doug Lea 提出了明显的反对意见)。所以是的,OP 对 NPE 导致管道爆炸的担忧是有道理的。

    Tony Hoare 将空值称为"Billion Dollar Mistake." 并非没有道理。处理空值是一件非常痛苦的事情。即使使用经典集合(不考虑 lambda 或流),空值也是有问题的。正如fge 在评论中提到的那样,一些集合允许空值,而其他集合则不允许。对于允许空值的集合,这会在 API 中引入歧义。例如,对于Map.get(),返回空值表示该键存在且其值为空,或者该键不存在。人们必须做额外的工作来消除这些情况的歧义。

    null 通常用于表示没有值。针对 Java SE 8 提出的处理此问题的方法是引入一个新的 java.util.Optional 类型,它封装了值的存在/不存在,以及提供默认值、抛出异常或调用函数的行为,等等,如果该值不存在。 Optional 只被新的 API 使用,不过,系统中的所有其他东西仍然必须忍受空值的可能性。

    我的建议是尽可能避免实际的空引用。鉴于如何可能存在“空”水獭,从示例中很难看出。但如果有必要,OP 建议过滤掉空值或将它们映射到哨兵对象(Null Object Pattern)是很好的方法。

    【讨论】:

    • 为什么要避免空值?它们广泛用于数据库中。
    • @RaffiKhatchadourian 是的,数据库中使用了空值,但同样存在问题。请参阅 thisthis 并阅读所有答案和 cmets。还要考虑 SQL null 对布尔表达式的影响:en.wikipedia.org/wiki/… ...这是查询错误的丰富来源。
    • @RaffiKhatchadourian 因为null 很烂,这就是原因。会计。在我看到的一项调查中(找不到),NPE 是 Java 中的第一个例外。 SQL不是高级编程语言,肯定不是函数式的,它鄙视null,所以无所谓。
    【解决方案4】:

    如果您只想从流中过滤空值,您可以简单地使用对java.util.Objects.nonNull(Object) 的方法引用。从其文档中:

    此方法存在用作Predicatefilter(Objects::nonNull)

    例如:

    List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);
    
    list.stream()
        .filter( Objects::nonNull )  // <-- Filter out null values
        .forEach( System.out::println );
    

    这将打印:

    Foo
    Bar
    

    【讨论】:

      【解决方案5】:

      如何避免 null 的示例,例如在 groupingBy 之前使用过滤器

      在 groupingBy 之前过滤掉空实例。

      这是一个例子
      MyObjectlist.stream()
                  .filter(p -> p.getSomeInstance() != null)
                  .collect(Collectors.groupingBy(MyObject::getSomeInstance));
      

      【讨论】:

        【解决方案6】:

        如果您不想迭代两次(过滤器 + 地图或任何)。试试这个。

        private static void trimAll() {
            String[] emtLne = {"", " ", "  cc  ", null, "      xx   "};
            System.out.println(Arrays.stream(emtLne).map(val -> (val != null) ? val.trim() : "").collect(Collectors.joining()));
        }
        

        输出
        ccxx

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-30
          • 1970-01-01
          • 1970-01-01
          • 2016-09-22
          • 2012-10-03
          相关资源
          最近更新 更多