1. 概述
在这个简短的教程中,我们将解释如何在使用流API时避免NoSuchElementException
。
首先,我们会解释异常的主要原因,然后通过实际例子展示如何重现和修复它。
2. 异常的原因
深入理解之前,先了解一下这个异常的含义。
简单来说,NoSuchElementException
会在请求的元素不存在时抛出。例如,尝试访问一个不存在或不存在的元素会导致此异常。
在使用流API时,通常在空的Optional
实例上调用get()
方法是导致NoSuchElementException
的常见原因之一。
3. 引发异常的过程
现在我们了解了异常,接下来实践一下看看如何在实际操作中重现它。
例如,我们可以创建一个名字列表并使用流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();
}
如图所示,我们使用了filter()
方法来查找名字"Emma",并且接着使用findFirst()
方法获取第一个找到的元素,或者如果过滤后的流为空,则返回一个空的Optional
。
在这里,我们的列表中不包含名字"Emma",所以findFirst()
返回一个空的Optional
。由于我们试图获取一个不存在的名字,且空的Optional
没有值,因此测试用例会因NoSuchElementException
异常失败。
4. 避免异常
现在让我们看看如何修复这个异常。最简单的方法是在调用get()
方法之前检查Optional
实例中是否有值。
幸运的是,流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);
}
这里,我们在调用get()
方法前使用isPresent()
确保Optional
实例中有值。这样可以避免NoSuchElementException
异常。
请注意,使用isPresent()
会带来if-else语句的开销。那么,是否可以做得更好呢?当然可以!
通常来说,最好的做法是使用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);
}
如上所述,这种方法提供了一种更方便、直接的方式来避免NoSuchElementException
。
另外,我们也可以使用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);
}
与orElse()
不同,orElseGet()
接受一个供应者作为参数。另一个关键区别是,orElse()
在所有情况下都会执行,即使Optional
实例已经有值。然而,orElseGet()
只有在Optional
值不存在时才会执行。
关于orElse()
和orElseGet()
方法的区别,请参阅我们的文章详细讨论。
5. 避免NoSuchElementException
的最佳实践
总之,在使用流API时,要避免NoSuchElementException
异常,需要注意以下几点:
- 在调用
get()
方法之前始终检查返回的流或Optional
是否不为空。 - 尝试使用
orElse()
或orElseGet()
定义备选值。 - 在调用流的终端操作之前,先进行过滤。
6. 总结
在这篇短文中,我们探讨了在使用流API时避免NoSuchElementException
异常的不同方法。
在过程中,我们展示了如何重现异常以及如何通过实际例子避免它。
如往常一样,所有示例的完整源代码可在GitHub上找到。