Handling NullPointerException in findFirst() When the First Element Is Null – 当第一个元素为空时,在 findFirst() 中处理 NullPointerException

最后修改: 2023年 12月 12日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this short tutorial, we’ll explore different ways of avoiding NullPointerException when working with the findFirst() method.

在本简短教程中,我们将探讨在使用 NullPointerException 方法时避免 findFirst() 的不同方法。

First, we’ll explain what causes the method to fail with NullPointerException. Then, we’ll demonstrate how to reproduce and fix the exception using practical examples.

首先,我们将解释导致该方法出现 NullPointerException 异常的原因。然后,我们将使用实际示例演示如何重现和修复异常。

2. Explaining the Problem

2.解释问题

In short, a NullPointerException is thrown to signal that we’re doing some operation using null where an object is required.

简而言之,抛出 NullPointerException 表示我们正在使用 null 执行某些操作,而这些操作需要一个对象。

Typically, we use findFirst() to return an Optional instance holding the first element of a given stream. However, according to the documentation, the method throws NullPointerException if the first returned element is null.

通常,我们使用 findFirst() 返回一个 Optional 实例,其中包含给定流中的第一个元素。但是,根据文档,如果返回的第一个元素为null,该方法将抛出NullPointerException

So, the main question here is how to avoid the NullPointerException exception when the first element of our stream is null. Before diving deep and answering our question, let’s reproduce the exception.

因此,这里的主要问题是,当流的第一个元素为时,如何避免NullPointerException异常。在深入探讨并回答我们的问题之前,让我们重现一下异常。

3. Reproducing the NullPointerException

3.重现 NullPointerException 异常</em

For instance, let’s assume we have a list of String objects:

例如,假设我们有一个 String 对象列表:

List<String> inputs = Arrays.asList(null, "foo", "bar");

Now, let’s try to get the first element of our list using the findFirst() method:

现在,让我们尝试使用 findFirst() 方法获取列表中的第一个元素:

@Test(expected = NullPointerException.class)
public void givenStream_whenCallingFindFirst_thenThrowNullPointerException() {
    Optional<String> firstElement = inputs.stream()
      .findFirst();
}

As we can see, the test case fails with NullPointerException because the first selected element of our list is null.

正如我们所看到的,测试用例失败时会出现 NullPointerException ,因为我们列表中的第一个选定元素是 null

The Optional API states that it’s the caller’s responsibility to ensure that the value is not null because it doesn’t provide any way to distinguish between “the value is present but set to null” and “the value is not present”. This is why the documentation prohibits the scenario where null is returned when using findFirst().

Optional API 规定,调用者有责任确保值不是null,因为它没有提供任何方法来区分 “值存在但被设置为null“和 “值不存在”。这就是文档禁止在使用 findFirst() 时返回 null 的原因。

4. Avoiding the Exception

4.避免例外情况

The easiest way to avoid NullPointerException in this case is to filter the stream before calling the findFirst() method.

在这种情况下,避免 NullPointerException 的最简单方法是在调用 findFirst() 方法之前 过滤流。

So, let’s see how we can do this in practice:

那么,让我们看看如何在实践中做到这一点:

@Test
public void givenStream_whenUsingFilterBeforeFindFirst_thenCorrect() {
    Optional<String> firstNotNullElement = inputs.stream()
      .filter(Objects::nonNull)
      .findFirst();

    assertTrue(firstNotNullElement.isPresent());
}

Here, we used the Objects#nonNull method to filter only objects that are not null. That way, we ensure the selected first element is not null. As a result, we avoid NullPointerException.

在这里,我们使用 Objects#nonNull 方法仅过滤非 null 的对象。这样,我们就能确保所选的第一个元素不是 null。因此,我们避免了NullPointerException

Another option would be to use the Optional#ofNullable method before calling the findFirst() method.

另一种方法是在调用 findFirst() 方法之前使用Optional#ofNullable方法。

This method returns an Optional instance with the specified value if it’s not null. Otherwise, it returns an empty Optional.

如果Optional实例不是null,此方法将返回一个具有指定值的Optional实例。否则,它将返回一个Optional

So, let’s see it in action:

那么,让我们来看看它的实际效果吧:

@Test
public void givenStream_whenUsingOfNullableBeforeFindFirst_thenCorrect() {
    Optional<String> firstElement = inputs.stream()
      .map(Optional::ofNullable)
      .findFirst()
      .flatMap(Function.identity());

    assertTrue(firstElement.isEmpty());
}

As shown above, we map each element into an Optional object that accepts null with the help of the ofNullable() method. Then, we get the first mapped element using findFirst().

如上图所示,在 ofNullable() 方法的帮助下,我们将每个元素映射到一个接受 nullOptional 对象中。然后,我们使用 findFirst() 获取第一个映射的元素。

The returned element denotes an Optional of an Optional since findFirst() returns an Optional. This is why we used flapMap() to flatten the nested Optional.

由于findFirst()返回的是一个Optional,因此返回的元素表示一个OptionalOptional。这就是为什么我们使用 flapMap() 来扁平嵌套的 Optional 的原因。

Please note that Function#identity always returns its input argument. In our case, the method returns null because it’s the first element of our list.

请注意,Function#identity 总是返回其输入参数。在我们的例子中,该方法返回 null 因为它是我们列表的第一个元素。

5. Conclusion

5.结论

In this short article, we explained how to avoid NullPointerException when working with the findFirst() method.

在这篇短文中,我们介绍了如何在使用 findFirst() 方法时避免 NullPointerException 异常。

Along the way, we showcased how to reproduce and solve the exception using practical examples.

一路上,我们用实际案例展示了如何重现和解决异常情况。

As always, the full source code of the examples is available over on GitHub.

一如既往,示例的完整源代码可在 GitHub 上获取。