97

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

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

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 对象来处理。

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

4

5 回答 5

115

尽管答案是 100% 正确的,但有一个小建议可以使用Optionalnull改进列表本身的案例处理:

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

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

于 2016-08-29T14:58:07.283 回答
71

斯图尔特的回答提供了一个很好的解释,但我想提供另一个例子。

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

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

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

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

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

来自 Stuart 链接的邮件列表讨论: Brian Goetz on Streams 中的空值

于 2014-06-21T15:26:01.747 回答
45

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

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

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

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

于 2013-06-14T18:59:21.423 回答
19

如果您只想从流中过滤空值,您可以简单地使用对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
于 2016-03-04T19:48:51.787 回答
7

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

在 groupingBy 之前过滤掉空实例。

这是一个例子

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
于 2015-08-25T15:23:58.593 回答