How to Avoid NoSuchElementException in Stream API – 如何避免流应用程序接口中出现 NoSuchElementException 异常

最后修改: 2023年 11月 17日

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

1. Overview

1.概述

In this short tutorial, we’ll explain how to avoid NoSuchElementException when working with the Stream API.

在本简短教程中,我们将介绍如何在使用 Stream API 时避免 NoSuchElementException 异常。

First, we’re going to explain the main cause of the exception. Then, we’ll showcase how to reproduce and fix it using practical examples.

首先,我们将解释异常的主要原因。然后,我们将使用实际示例展示如何重现和修复异常。

2. The Cause of the Exception

2.例外情况的原因

Before delving deep into the details, let’s understand what the exception means.

在深入探讨细节之前,我们先来了解一下例外情况的含义。

In short, NoSuchElementException is thrown to signal that the requested element doesn’t exist. For instance, trying to access an element that is not available or present will lead to this exception.

简而言之,NoSuchElementException 的抛出表示请求的元素不存在。例如,尝试访问一个不可用或不存在的元素将导致此异常。

Typically, calling the get() method on an empty Optional instance is one of the most common causes of NoSuchElementException when working with the Stream API.

通常,在使用流 API 时,在空 Optional 实例上调用 get() 方法是导致 NoSuchElementException 的最常见原因之一。

3. Producing the Exception

3.例外情况的产生

Now that we know what the exception is, let’s go down the rabbit hole and see how to reproduce it in practice.

既然我们已经知道了例外情况是什么,那就让我们走进兔子洞,看看如何在实践中重现它。

For example, let’s create a list of names and filter it using the Stream API:

例如,让我们创建一个姓名列表,并使用流 API 对其进行过滤:

@Test(expected = NoSuchElementException.class)
public void givenEmptyOptional_whenCallingGetMethod_thenThrowNoSuchElementException() {
    List<String> names = List.of("William", "Amelia", "Albert", "Philip");
    Optional<String> emptyOptional = names.stream()
      .filter(name -> name.equals("Emma"))
      .findFirst();

    emptyOptional.get();
}

As we can see, we used the filter() method to find the name “Emma”. Furthermore, we chained with the findFirst() method to get an Optional containing the first found element or an empty Optional if the filtered stream is empty.

我们可以看到,我们使用 filter() 方法找到了 “Emma” 这个名字。此外,我们使用 findFirst() 方法链来获取包含第一个找到的元素的 Optionalempty Optional 方法(如果过滤后的流为空)。

Here, our list doesn’t contain the name “Emma”, so findFirst() returns an empty Optional. The test case fails with the NoSuchElementException exception because we’re trying to get a name that doesn’t exist and an empty Optional doesn’t hold any value.

在这里,我们的列表不包含名称 “Emma” ,因此 findFirst() 返回一个空 Optional由于我们试图获取一个不存在的名称,而空Optional不包含任何值,因此测试用例出现 NoSuchElementException 异常而失败

4. Avoiding the Exception

4.避免例外情况

Now, let’s see how to fix the exception. The easiest way would be to check if there’s a value present in our Optional instance before calling the get() method.

现在,让我们来看看如何修复异常。最简单的方法是在调用 get() 方法之前,检查 Optional 实例中是否存在值。

Fortunately, the Stream API provides the isPresent() method specifically for this purpose. So, let’s see it in action:

幸运的是,流 API 专门为此提供了 isPresent() 方法。那么,让我们来看看它的实际应用:

@Test
public void givenEmptyOptional_whenUsingIsPresentMethod_thenReturnDefault() {
    List<String> names = List.of("Tyler", "Amelia", "James", "Emma");
    Optional<String> emptyOptional = names.stream()
      .filter(name -> name.equals("Lucas"))
      .findFirst();

    String name = "unknown";
    if (emptyOptional.isPresent()) {
        name = emptyOptional.get();
    }

    assertEquals("unknown", name);
}

Here, we used isPresent() to make sure that there’s a value inside our Optional instance before calling the get() method. That way, we avoid the NoSuchElementException exception.

在这里,我们使用 isPresent() 来确保在调用 get() 方法之前,我们的 Optional 实例中存在一个值。这样,我们就避免了 NoSuchElementException 异常。

Please notice that the use of isPresent() comes with the cost of the if-else statements. So, can we do it better? Yes!

请注意,isPresent() 的使用是以 if-else 语句为代价的。那么,我们能做得更好吗?可以!

Typically, the best way to go is to use the orElse() method. In short, this method returns the value if it’s present, or the given fallback argument otherwise:

通常,最好的方法是使用 orElse() 方法。简而言之,如果存在该值,该方法将返回该值,否则将返回给定的后备参数

@Test
public void givenEmptyOptional_whenUsingOrElseMethod_thenReturnDefault() {
    List<String> names = List.of("Nicholas", "Justin", "James");
    Optional<String> emptyOptional = names.stream()
      .filter(name -> name.equals("Lucas"))
      .findFirst();

    String name = emptyOptional.orElse("unknown");

    assertEquals("unknown", name);
}

As shown above, this method offers a more convenient and straightforward way to avoid NoSuchElementException.

如上所示,该方法提供了一种更方便、更直接的方法来避免 NoSuchElementException 的发生。

Alternatively, we can use the orElseGet() method to achieve the same outcome:

或者,我们可以使用 orElseGet() 方法来实现相同的结果:

@Test
public void givenEmptyOptional_whenUsingOrElseGetMethod_thenReturnDefault() {
    List<String> names = List.of("Thomas", "Catherine", "David", "Olivia");
    Optional<String> emptyOptional = names.stream()
      .filter(name -> name.equals("Liam"))
      .findFirst();

    String name = emptyOptional.orElseGet(() -> "unknown");

    assertEquals("unknown", name);
}

Unlike orElse(), orElseGet() accepts a supplier as a parameter. Another key difference is that orElse() is executed in all cases, even if the Optional instance has a value. However, orElseGet() is only executed when the Optional value isn’t present.

orElse() 不同,orElseGet() 接受 supplier 作为参数。另一个主要区别是,orElse() 在所有情况下都会被执行,即使 Optional 实例有一个值。但是,orElseGet() 只有在 Optional 值不存在时才会被执行

Please note that our article on the difference between the orElse() and orElseGet() methods does a great job of covering the topic.

请注意,我们的文章orElse()orElseGet()方法之间的区别很好地涵盖了这一主题。

5. Best Practices to Avoid NoSuchElementException

5.避免 NoSuchElementException 的最佳做法

In a nutshell, there are several key points to keep in mind when working with the Stream API to avoid the NoSuchElementException exception:

简而言之,在使用流 API 时,有几个要点需要牢记,以避免出现 NoSuchElementException 异常:

  • Always check if the returned stream/optional is not empty before calling the get() method.
  • Try to define a fallback value using orElse() or orElseGet().
  • Use a filter before calling any terminal operation on a stream.

6. Conclusion

6.结论

In this short article, we explored different ways of avoiding the exception NoSuchElementException when working with the Stream API.

在这篇短文中,我们探讨了在使用流 API 时避免 NoSuchElementException 异常的不同方法。

Along the way, we illustrated how to reproduce the exception and how to avoid it using practical examples.

一路上,我们用实际例子说明了如何重现异常和如何避免异常。

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

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