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

nev*_*gqs 77 java optional java-8 java-stream

为什么这会抛出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
Run Code Online (Sandbox Code Playgroud)

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

编辑:更新orElse()不那么模棱两可.

das*_*ght 63

这样做的原因是Optional<T>在回报中使用.不允许包含可选项null.从本质上讲,它无法区分"它不在那里"和"它在那里,但它被设置为null"的情况.

这就是为什么文档明确禁止在以下情况null中选择的情况findFirst():

抛出:

NullPointerException - 如果选择的元素是 null

  • 跟踪一个值是否确实存在,并且在"Optional"的实例中存在私有布尔值.无论如何,我认为我接近咆哮 - 如果语言不支持它,它不支持它. (2认同)
  • @neverendingqs当然,使用`boolean`来区分这两种情况会非常有意义.对我来说,看起来使用`Optional <T>`这是一个值得怀疑的选择. (2认同)

Hol*_*ger 43

正如已经讨论过的API设计师不要认为开发商要治疗null值和缺失值的方法相同.

如果您仍想这样做,可以通过应用序列明确地执行此操作

.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
Run Code Online (Sandbox Code Playgroud)

到溪边.如果没有第一个元素或者第一个元素是,则在两种情况下结果都是空的可选项null.所以在你的情况下,你可以使用

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);
Run Code Online (Sandbox Code Playgroud)

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

这与您更新的问题没什么不同.你只需要更换"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);
Run Code Online (Sandbox Code Playgroud)

现在,firstString将是null一个元素存在但是,null并且它将"StringWhenListIsEmpty"在没有元素存在时.

  • @RayJasson 好问题。我想,这是一些漏掉的测试的剩余部分。应该没有什么影响。我把它去掉了。 (2认同)

Nat*_*han 12

下面的代码替换findFirst()limit(1),并替换orElse()reduce():

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);
Run Code Online (Sandbox Code Playgroud)

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

这个解决方案OptionalBinaryOperator优点是没有分配,lambda不会分配任何东西.


Mat*_*tos 7

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

就像是

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

  • 如果 `strings` 中的第一项为 `null`,我希望 `firstString` 为 `null`。 (2认同)
  • 不幸的是,它使用的不是无效的```Optional.of```。您可以将“ map”映射到“ Optional.ofNullable”,然后使用“ findFirst`”,但最终会出现一个Optional of Optional (2认同)