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

cle*_*ent 78 java null java-8 java-stream

你好Java开发人员,

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

以下是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());
Run Code Online (Sandbox Code Playgroud)

我的问题是如果在Set内部迭代的中间,其中一个otter为null会发生什么?

我希望抛出NullPointerException,但也许我仍然坚持以前的开发范例(非功能),有人可以启发我应该如何处理它?

如果这确实抛出了NullPointerException,我发现该功能非常危险,只能如下所示:

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

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

谢谢!

Joh*_*nny 72

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

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

Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)listOfStuffnull为null 时,该部分将允许您很好地处理该情况并返回emptyList而不是使用NullPointerException失败.

  • 我喜欢这个,避免显式检查null. (2认同)
  • 这看起来最清楚..很好,正是我所需要的 (2认同)
  • @AshburnRK 这是一个不好的做法。您应该返回一个空列表。 (2认同)

bpa*_*rry 65

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

尝试reduce在包含空值的Stream上执行此操作时遇到此问题(实际上它是LongStream.average(),这是一种减少类型).由于average()返回OptionalDouble,我假设Stream可以包含空值,但抛出了NullPointerException.这是由于斯图尔特对null诉空的解释.

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

list.stream()
    .filter(o -> o != null)
    .reduce(..);
Run Code Online (Sandbox Code Playgroud)

或者正如tangens指出的那样,使用Java API提供的谓词:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);
Run Code Online (Sandbox Code Playgroud)

从邮件列表讨论Stuart链接: Brian Goetz在Streams中的null


Stu*_*rks 40

目前的想法似乎是"容忍"空值,即允许它们,尽管有些操作不那么宽容,最终可能会抛出NPE.请参阅Lambda Libraries专家组邮件列表中的空值讨论,特别是此消息.随后出现了关于选项#3的共识(Doug Lea的一个值得注意的反对意见).所以,是的,OP对NPE管道爆炸的担忧是有效的.

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

null的通常用法是表示没有值.处理Java SE 8的方法是引入一种新java.util.Optional类型,它封装了值的存在/不存在,以及提供默认值,抛出异常或调用函数等的行为.价值不存在.Optional但是,只有新的API使用它,系统中的其他所有东西仍然需要提供null的可能性.

我的建议是尽可能避免实际的空引用.鉴于可能存在"无效"水獭的例子,很难从这个例子中看出来.但如果有必要,OP建议过滤掉空值,或将它们映射到一个标记对象(空对象模式)是很好的方法.

  • @RaffiKhatchadourian是的,nulls在数据库中使用,但同样有问题.见[this](http://stackoverflow.com/questions/163434/are-nulls-in-a-relational-database-okay)和[this](http://dba.stackexchange.com/questions/5222/ why-shouldnt-we-allow-nulls)并且阅读**所有**的答案和评论.还要考虑SQL null对布尔表达式的影响:https://en.wikipedia.org/wiki/Null_%28SQL%29#Law_of_the_excluded_fourth_.28in_WHERE_clauses.29 ...这是查询错误的丰富来源. (4认同)
  • 为什么要避免空值?它们被广泛用于数据库中。 (2认同)

And*_*mas 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 );
Run Code Online (Sandbox Code Playgroud)

这将打印:

Foo
Bar
Run Code Online (Sandbox Code Playgroud)


Ila*_*n M 7

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

在groupingBy之前过滤掉空实例.

这是一个例子

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
Run Code Online (Sandbox Code Playgroud)