如果findFirst()找到的第一个元素为null,为什么会抛出NullPointerException?


89

为什么这样抛出java.lang.NullPointerException

List<String> strings = new ArrayList<>();
        strings.add(null);
        strings.add("test");

        String firstString = strings.stream()
                .findFirst()      // Exception thrown here
                .orElse("StringWhenListIsEmpty");
                //.orElse(null);  // Changing the `orElse()` to avoid ambiguity

中的第一项stringsnull,这是一个完全可以接受的值。此外,findFirst()返回Optional,这对于findFirst()能够处理nulls更有意义。

编辑:更新了orElse()以减少歧义。


4
null并非完全可以接受的值...请改用“”
Michele Lacorte 2015年

1
@MicheleLacorte尽管我在String这里使用,但是如果它是表示数据库中列的列表,该怎么办?该列的第一行的值可以为null
neverendingqs 2015年

是的,但是在Java中,null是不可接受的
。.使用

10
一般来说,@ MicheleLacortenull在Java中是一个完全可以接受的值。特别地,它是一个有效元素ArrayList<String>。但是,像其他任何值一样,使用它可以完成的工作也受到限制。“永远不要使用null”不是有用的建议,因为您无法避免。
John Bollinger

@NathanHughes-我怀疑您打来电话时findFirst(),您什么都不想做。
neverendingqs 2015年

Answers:


72

这样做的原因是Optional<T>在返回中使用。不允许包含Optional null。本质上,它无法区分“不存在”和“存在”,但是将其设置为null“”。

这就是为什么该文件明确禁止时的情况null中选择findFirst()

抛出:

NullPointerException -如果所选元素是 null


3
跟踪值是否确实存在的实例中包含私有布尔值将很容易Optional。无论如何,我认为我正在抱怨-如果该语言不支持,则不支持。
neverendingqs 2015年

2
@neverendingqs绝对可以,使用aboolean来区分这两种情况是很有意义的。在我看来,使用Optional<T>此处是一个值得怀疑的选择。
Sergey Kalinichenko

1
@neverendingqs除了滚动自己的null之外,我也想不出其他好看的替代方法,这也不理想。
Sergey Kalinichenko

1
我最后写了一个私有方法,该方法从任何Iterable类型获取迭代器,检查hasNext()并返回适当的值。
neverendingqs 2015年

1
我认为,如果findFirst在PO的情况下返回空的Optional值更有意义
丹尼

47

正如已经讨论过的,API设计人员并不假定开发人员要以null相同的方式对待值和缺少值。

如果您仍然想要这样做,可以通过应用序列来明确地执行

.map(Optional::ofNullable).findFirst().flatMap(Function.identity())

到流。如果没有第一个元素或如果第一个元素是,则在两种情况下,结果都是一个空的可选null。因此,您可以使用

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);

null如果第一个元素不存在或,则获取一个值null

如果要区分这些情况,可以简单地省略以下flatMap步骤:

Optional<String> firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
                   firstString.orElse("first element is null"));

这与您更新的问题没有太大不同。你只需要更换"no such element""StringWhenListIsEmpty""first element is null"null。但是,如果您不喜欢条件式,也可以这样实现:

String firstString = strings.stream().skip(0)
    .map(Optional::ofNullable).findFirst()
    .orElseGet(()->Optional.of("StringWhenListIsEmpty"))
    .orElse(null);

现在,firstString将是null如果元素存在但仍然存在null,它将是"StringWhenListIsEmpty"当元素不存在时。


抱歉,我意识到我的问题可能暗示我想返回null以下内容:1)第一个元素为null2)列表中不存在任何元素。我已更新问题以消除歧义。
neverendingqs 2015年

1
在第三个代码段中,Optional可以将分配给null。由于Optional应该是“值类型”,因此它永远不能为null。并且Optional绝不能与进行比较==。该代码在Java 10中可能会失败:)或在Java中引入值类型时都会失败。
宇2015年

1
@ bayou.io:文档中没有说不能引用值类型null,并且决不能将实例进行比较==,但是可以测试null使用该引用,==因为这是测试它的唯一方法null。我看不到null现有代码如何过渡到“从不”,因为所有实例变量和数组元素的默认值都是null。该代码段当然不是最佳代码,但也不是将nulls视为现值的任务。
Holger

看到约翰玫瑰-也不能与“==”操作符,甚至没有与空相比
中宇

1
由于此代码使用的是通用API,因此该概念称为框式表示形式,可以是null。但是,由于这种假设的语言更改将导致编译器在此处发出错误(而不是无声地破坏代码),因此我可以接受这样的事实,即它可能必须适用于Java10。我想,StreamAPI将看起来也完全不同……
Holger 2015年

18

您可以java.util.Objects.nonNull在找到之前使用过滤列表

就像是

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

1
我想firstString成为null第一个项目stringsnull
neverendingqs

2
不幸的是,它使用Optional.of不是null安全的。你可以mapOptional.ofNullable ,然后用findFirst,但你最终将与可选的选项
马托斯

14

以下代码替换为findFirst()limit(1)并替换orElse()reduce()

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);

limit()只允许1个元素到达reduce。在BinaryOperator传递到reduce返回的是1元,否则"StringWhenListIsEmpty",如果没有元素到达reduce

此解决方案OptionalBinaryOperator优点在于,它不会分配,而lambda不会分配任何东西。


1

可选应该是“值”类型。(请阅读javadoc中的详细信息:) JVM甚至可以Optional<Foo>用just替换所有内容Foo,从而消除所有装箱和拆箱费用。甲null富指空的Optional<Foo>

一种可能的设计是允许Optional具有null值,而无需添加布尔标志-只需添加一个哨兵对象。(甚至可以this用作哨兵;请参阅Throwable.cause)

Optional不能包装null的决定不是基于运行时成本的。这是一个极具争议的问题,您需要挖掘邮件列表。这个决定并不使所有人信服。

无论如何,由于Optional不能包装null值,因此在类似的情况下,它会将我们推到了一个角落findFirst。他们必须认为,空值非常少见(甚至认为Stream应当禁止空值),因此,将异常引发到空值而不是空流上更为方便。

解决方法是装箱null,例如

class Box<T>
    static Box<T> of(T value){ .. }

Optional<Box<String>> first = stream.map(Box::of).findFirst();

(他们说解决每个OOP问题的方法是引入另一种类型:)


1
无需创建其他Box类型。该Optional类型本身可以达到这个目的。请参阅我的答案作为示例。
Holger 2015年

@Holger-是的,但这可能会造成混淆,因为这不是Optional的预期目的。在OP的情况下,它null是一个有效值,与其他值一样,因此无需特殊处理。(直到以后的某个时间:)
ZhongYu Yu'9
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.