Return First Non-null Value in Java – 在 Java 中返回第一个非空值

最后修改: 2023年 11月 27日

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

1. Overview

1.概述

In this tutorial, we’ll learn how to return the first non-null element from a list or sequence of data.

在本教程中,我们将学习如何从列表或数据序列中返回第一个非元素。

We’ll also explore lazy evaluation when returning the first non-null from a chain of expensive methods. Finally, we’ll discover how the use of the Optional class will require us to return the first non-empty Optional instead.

我们还将探讨从一连串昂贵的方法中返回第一个非方法时的懒惰评估。最后,我们将发现 Optional 类的使用将如何要求我们 返回第一个非空 Optional 而不是

2. for Loop

2.for 循环

Before the introduction of functional programming in Java 8, it was common to use a for loop to return the first non-null element from a list.

在 Java 8 引入函数式编程之前,通常使用 for 循环来返回列表中第一个非元素。

Let’s consider a list with null for the first element:

让我们来看看第一个元素为 null 的列表:

List<String> objects = Arrays.asList(
    null,
    "first non null",
    "second non null"
);

To return the first non-null element, we could use a traditional for loop to iterate and check each element for nullability via a simple if statement:

要返回第一个非元素,我们可以使用传统的 for 循环进行遍历,并通过一个简单的 if 语句检查每个元素是否为空:

String object = null; 
for (int i = 0; i < objects.size(); i++) {
    if (objects.get(i) != null) {
        object = objects.get(i);
        break;
    }
}

3. Stream API

3.流应用程序接口

With the introduction of the Stream API in Java 8, we’re now able to accomplish many common patterns in a more readable, simplistic, and declarative fashion.

随着 Stream API 在 Java 8 中的引入,我们现在能够以更易读、更简单和更声明化的方式实现许多常见模式。

To find the first non-null element, we stream our list sequentially by invoking the stream() method and search for the first non-null element:

为了找到第一个非元素,我们通过调用 stream() 方法按顺序对列表进行流式处理,并搜索第一个非元素:

Optional<String> object = objects
  .stream()
  .filter(o -> o != null)
  .findFirst();

We use the intermediate filter() operation to filter our stream based on a Predicate, which in our case is a simple null check lambda o -> o! = null. Finally, we invoke the terminal findFirst() operation to return the first element that satisfied the preceding operations or an empty Optional.

我们使用中间 filter() 操作来根据 Predicate 过滤我们的流,在我们的例子中,这是一个简单的 null 检查 lambda o -> o!= 最后,我们调用终端 findFirst() 操作,返回满足前面操作的第一个元素或空 Optional

In pursuit of enhanced readability, we could instead use Objects.nonNull() as a method reference in place of the lambda expression:

为了提高可读性,我们可以使用 Objects.nonNull() 作为方法引用来代替 lambda 表达式:

Optional<String> object = objects
  .stream()
  .filter(Objects::nonNull)
  .findFirst();

4. Lazy Evaluation of Methods That May Return null

4.对可能返回 null 的方法进行懒评估

Throughout this article, we’ve assumed that we want to return the first non-null item from a sequence of data that’s readily available. But what if the methods used to obtain our values are expensive to evaluate? Instead, we may want to lazily evaluate a sequential chain of methods until we obtain the first non-null for performance reasons.

在本文中,我们一直假设要从一连串数据中返回第一个非项。但是,如果用于获取值的方法的评估成本很高怎么办?相反,出于性能考虑,我们可能希望快速评估一连串方法,直到获得第一个非

Let’s consider the following methods, which we’ll pretend are expensive to compute:

让我们考虑一下下面的方法,我们就当它们的计算成本很高:

String methodA() {
    return null;
}

String methodB() {
    return "first non null";
}

String methodC() {
    return "second non null";
}

Before Java 8, we may have used a series of if statements:

在 Java 8 之前,我们可能会使用一系列 if 语句:

String object = methodA();
if (object == null) {
    object = methodB();
}

if (object == null) {
    object = methodC();
}

With the Stream API, we can take advantage of the functional interface Supplier to achieve lazy evaluation of our methods:

通过 Stream API,我们可以利用函数接口 Supplier 来实现方法的懒评估

Optional<String> object = Stream
  .<Supplier<String>>of(
      this::methodA, 
      this::methodB,
      this::methodC)
  .map(Supplier::get)
  .filter(Objects::nonNull)
  .findFirst();

In our stream pipeline, an expensive method is only evaluated when get() is invoked on the Supplier function object as part of the map() operation. We ensure lazy evaluation by using a sequential stream. Each stream element is checked by the intermediate filter() condition and the stream terminates as soon as we find the first non-null element that satisfies the condition.

在我们的流管道中,只有在作为 map() 操作的一部分对 Supplier 函数对象调用 get() 时,才会对昂贵的方法进行评估。我们通过使用顺序流来确保懒评估。每个流元素都要经过中间 filter() 条件的检查,一旦我们找到第一个满足条件的非元素,流就会终止。

5. External Libraries

5.外部库

Instead of writing our own implementations, we can also leverage popular external libraries that address the issue.

我们也可以利用流行的外部库来解决这个问题,而不是自己编写实现方法。

5.1. Apache Commons Lang 3

5.1.阿帕奇共享资源 3

To use Apache Commons Lang 3, we need to add the following dependency to our pom.xml:

要使用 Apache Commons Lang 3,我们需要在 pom.xml 中添加以下依赖关系:

<dependency> 
    <groupId>org.apache.commons</groupId> 
    <artifactId>commons-lang3</artifactId> 
    <version>3.14.0</version> 
</dependency>

Given a single reference which may or may not be null, we can use ObjectUtils.getIfNull() to obtain the value if it’s non-null or return the value from an alternative method that’s lazily evaluated:

对于可能是也可能不是 null 的单个引用,我们可以使用 ObjectUtils.getIfNull() 来获取非null 的值,或者使用另一种方法来返回值,这种方法会对引用进行懒惰评估: </em

ObjectUtils.getIfNull(object, this::methodB);

Given a list of objects, we can utilize ObjectUtils.firstNonNull() which takes a varargs:

给定一个对象列表,我们可以使用 ObjectUtils.firstNonNull(),它需要一个 varargs:

@Test
void givenListOfObjects_whenUsingApacheCommonsLang3_thenReturnFirstNonNull() {
    String object = ObjectUtils.firstNonNull(objects.toArray(new String[0]));

    assertEquals("first non null", object);
}

If all the arguments are null, then null is returned.

如果所有参数都为 ,则返回

Further, we can also lazily evaluate our nullable methods with ObjectUtils.getFirstNonNull():

此外,我们还可以使用 ObjectUtils.getFirstNonNull() 对我们的 nullable 方法进行懒惰式评估:

ObjectUtils.getFirstNonNull(this::methodA, this::methodB, this::methodC);

5.2. Google Guava

5.2谷歌Java

To use Google Guava, we need to add the following dependency to our pom.xml:

要使用 Google Guava,我们需要在 pom.xml 中添加以下依赖关系:

<dependency> 
    <groupId>com.google.guava</groupId> 
    <artifactId>guava</artifactId> 
    <version>32.1.3-jre</version> 
</dependency>

Given two references, we can use MoreObjects.firstNonNull():

如果有两个引用,我们可以使用 MoreObjects.firstNonNull()

@Test
void givenTwoObjects_whenUsingGoogleGuavaMoreObjects_thenReturnFirstNonNull() {
    String nullObject = null;
    String nonNullObject = "first non null";
    String object = MoreObjects.firstNonNull(nullObject, nonNullObject);

    assertEquals("first non null", object);
}

However, if both arguments are null then a NullPointerExecption is thrown.

但是,如果两个参数都为,则会抛出NullPointerExecption

Given a list, we can return the first non-null using Iterables.find() with Predicates.nonNull():

如果给定一个列表,我们可以使用 Iterables.find()Predicates.nonNull() 返回第一个非空值

@Test
void givenListOfObjects_whenUsingGoogleGuavaIterables_thenReturnFirstNonNull() {
    String object = Iterables.find(objects, Predicates.notNull());

    assertEquals("first non null", object);
}

6. Optional

6.可选项

This article would be amiss if it didn’t mention the use of the Optional type. This class was introduced in Java 8 specifically to address the shortcomings of null references.

如果本文不提及 Optional 类型的使用,那就有失公允了。Java 8 中引入了该类,专门用于解决 null 引用的缺点。

The Optional type allows developers to signify explicitly that a method or variable may or may not have a value. As a result, our methods which previously returned a null or value now return an Optional instead.

Optional类型允许开发人员明确表示方法或变量可能有值,也可能没有值。因此,以前返回 null 或值的方法现在会返回 Optional 值。

Thus, our previous problem of ‘returning the first non-null value’ transformed into a subtly different problem. How do we return the first non-empty Optional?

因此,我们之前遇到的 “返回第一个非值 “的问题变成了一个微妙的不同问题。我们如何返回第一个非空 可选值

Let’s consider the following list that has an empty first element:

让我们来看看下面这个第一个元素为空的列表:

List<Optional<String>> optionals = Arrays.asList(
    Optional.<String> empty(), 
    Optional.of("first non empty"), 
    Optional.of("second non empty")
);

We can stream our list and search for the first non-empty element:

我们可以对列表进行流式处理,搜索第一个非空元素:

@Test
void givenListOfOptionals_whenStreaming_thenReturnFirstNonEmpty() {
    Optional<String> object = optionals.stream()
      .filter(Optional::isPresent)
      .map(Optional::get)
      .findFirst();

    assertThat(object).contains("first non empty");
}

We check if each given Optional has a value using the ifPresent() method. If an element satisfies this predicate, then we can safely obtain the value using get() as part of the map() intermediate operation.

我们使用 ifPresent() 方法检查每个给定的 Optional 是否具有值。如果某个元素满足此谓词,那么我们就可以使用 get() 作为 map() 中间操作的一部分,安全地获取该值。

7. Conclusion

7.结论

In this article, we’ve explored how to return the first non-null using our own implementations as well as external libraries.

在本文中,我们探讨了如何使用自己的实现和外部库返回第一个非空值

We’ve also considered lazily evaluation when we want to return the first non-null from a chain of expensive methods.

当我们希望从一连串昂贵的方法中返回第一个非null时,我们也考虑过懒散地进行评估。

Finally, we’ve also demonstrated how to return the first non-empty Optional as this may be a more appropriate use case since its introduction in Java 8.

最后,我们还演示了如何返回第一个非空选项,因为自 Java 8 引入该选项以来,这可能是一个更合适的使用案例。

The code samples used in this article can be found over on GitHub.

本文中使用的代码示例可在 GitHub 上找到 over.