我们应该如何管理jdk8流中的空值


88

Java开发人员大家好,

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

这是《Java杂志》文章中给出的示例(这是一种水獭种群算法。):

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内部迭代的中间,水獭之一为null,会发生什么情况?

我希望抛出NullPointerException,但也许我还是停留在以前的开发范例(非功能性)中,有人可以启发我,因为应该如何处理?

如果这确实引发了NullPointerException,则我认为该功能非常危险,必须仅按以下方式使用:

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

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

谢谢!


3
我想说的是,程序员应该在这里做正确的事。JVM和编译器只能做很多事情。请注意,某些集合实现将不允许使用空值。
fge

18
您可以使用filter(Objects::nonNull)Objectsjava.utils
BENJ

Answers:


45

当前的想法似乎是“容忍”空值,也就是说,通常允许它们,尽管某些操作的容忍度较低,并且最终可能会抛出NPE。请参阅Lambda Libraries专家组邮件列表中有关null讨论,特别是此消息。随后出现了关于选项3的共识(道格·利阿提出了明显反对)。因此,是的,OP对NPE炸毁管道的担忧是正确的。

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

null的通常用法是表示不存在值。针对Java SE 8提出的解决方案是引入一种新java.util.Optional类型,该类型封装了值的存在/不存在以及提供默认值,抛出异常或调用函数等行为(如果有的话)。该值不存在。Optional只能由新的API使用,但是,系统中的所有其他内容仍然不得不忍受null的可能性。

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


3
为什么要避免空值?它们被广泛用于数据库中。
Raffi Khatchadourian

5
@RaffiKhatchadourian是的,数据库中使用了null,但同样存在问题。见这个这个和读取所有的答案和评论。还请考虑SQL null对布尔表达式的影响:en.wikipedia.org/wiki/………这是查询错误的丰富来源。
斯图尔特(Stuart Marks)

1
@RaffiKhatchadourian因为null很烂,所以。累积 在我看到的一项调查(找不到)中,NPE是Java中的第一大例外。SQL不是高级编程语言,当然也不是功能性的,它鄙视null,因此不在乎。
Abhijit Sarkar

91

尽管答案是100%正确,但建议null使用Optional来改善列表本身的大小写处理:

 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。


2
我喜欢这样,避免显式检查null。
克里斯(Chris)

1
这看起来很清楚..很好,正是我需要的东西
Abdullah Al Noman

它只是以不同的方式显式检查null。如果允许它为null,那么它应该是Optional,就是这样,对吗?禁止使用所有可选的空值。而且,返回空值而不是空列表是一种不好的做法,因此,如果看到这种情况,它会显示两种代码味道:没有使用Optionals,也没有返回空流。后者的可用时间超过20年,因此已经成熟...
Koos Gadellaa

如果我想返回空而不是空列表怎么办?
Ashburn RK

@AshburnRK,这是一个坏习惯。您应该返回一个空列表。
约翰尼

69

Stuart的答案提供了很好的解释,但是我想提供另一个示例。

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

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

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

或正如下所示,请使用Java API提供的谓词:

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

在邮件列表讨论中,Stuart链接了: Brian Goetz,关于Streams中的null


19

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

该方法已被用作谓词filter(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

7

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

在groupingBy之前过滤掉空实例。

这是一个例子

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.