Java 8中的多个空检查


99

我有下面的代码,对于多个空检查来说有点难看。

String s = null;

if (str1 != null) {
    s = str1;
} else if (str2 != null) {
    s = str2;
} else if (str3 != null) {
    s = str3;
} else {
    s = str4;
}

因此,我尝试使用Optional.ofNullable如下所示的方法,但是如果有人阅读了我的代码,仍然很难理解。用Java 8最好的方法是什么

String s = Optional.ofNullable(str1)
                   .orElse(Optional.ofNullable(str2)
                                   .orElse(Optional.ofNullable(str3)
                                                   .orElse(str4)));

在Java 9中,我们可以使用Optional.ofNullablewith OR,但是在Java8中,还有其他方法吗?


4
Java9 or语法 String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);看起来不如Stream.of我所愿。
纳曼

2
@ OleV.V。没错,OP已经意识到了这一点,并且正在寻求Java-8特有的功能。
纳曼

3
我知道用户正在要求Java-8特定的解决方案,但总的来说,我会选择StringUtils.firstNonBlank()
Mohamed Anees

3
问题是,Java 8 / streams并不是最佳解决方案。该代码确实闻起来像是按顺序进行重构,但是如果没有更多上下文,就很难说出来。对于初学者来说,为什么集合中可能没有如此紧密相关的三个对象呢?
比尔K,

2
@MohamedAneesA本来可以提供最佳答案(作为评论),但在这种情况下未指定StringUtils的来源。无论如何,如果您必须将它们作为一堆单独的字符串,并将其编码为vargs方法(例如“ firstNonBlank”),则是理想的选择,其中的语法将是一个数组,该数组使简单的for-each循环返回找到一个非空值琐碎且显而易见。在这种情况下,java 8流是一个令人讨厌的麻烦。他们会诱使您内联和复杂化一些本应是简单方法/循环的事情。
比尔K,

Answers:


172

您可以这样做:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);

16
这个。考虑您的需求,而不是您的需求。
托尔比约恩Ravn的安德森

21
速度开销是多少?创建Stream对象,调用4个方法,创建临时数组({str1, str2, str3})看上去比Java运行时可以优化的local if或慢得多?:javacJava运行时中是否有一些特定于Stream的优化,使其达到了最快的速度?:?如果没有,我将不建议在性能关键的代码中使用此解决方案。
pts

16
@pts这很有可能比?:代码要慢,我相信您应该在性能关键的代码中避免使用它。但是,它更具可读性,您应该在对性能要求不高的代码IMO中推荐它,我相信它可以构成99%以上的代码。
亚伦

7
@pts javac在运行时或运行时都没有特定于Stream的优化。这并不排除一般的优化,例如内联所有代码,然后消除多余的操作。原则上,最终结果可能与普通条件表达式一样有效,但是,最终结果不太可能到达那里,因为运行时仅在最热的代码路径上花费必要的精力。
Holger

22
很好的解决方案!为了增加可读性一点点,我建议添加str4到的参数Stream.of(...)和使用orElse(null)进行到底。
danielp '19

73

三元条件运算符怎么样?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;

50
实际上,他正在寻找“用Java8做到这一点的最佳方法”。这种方法可以在Java8中使用,因此这完全取决于OP所谓“最佳”的含义,以及他基于什么理由来决定哪种更好。
Stultuske '19

17
我通常不喜欢嵌套的三元组,但这看起来很干净。
JollyJoker

11
对于每天都不阅读嵌套三元运算符的任何人来说,这都是巨大的痛苦。
立方

2
@Cubic:如果您认为它很难解析,请写一个类似的评论// take first non-null of str1..3。一旦知道了它的作用,就很容易看到它。
彼得·科德斯

4
@Cubic然而,这是一个简单的重复模式。解析第2行后,无论包括多少种情况,您都将解析整个内容。一旦完成,您就已经学会了以简短而相对简单的方式表达十种不同情况的类似选择。下次看到?:梯子时,您会知道它的作用。(顺便说一句,函数式程序员将其称为(cond ...)子句:它始终是一个警戒,当警戒为true时,紧随其后使用适当的值。)
cmaster-恢复monica

35

您还可以使用循环:

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}

27

当前的答案很好,但您确实应该将其放在实用程序方法中:

public static Optional<String> firstNonNull(String... strings) {
    return Arrays.stream(strings)
            .filter(Objects::nonNull)
            .findFirst();
}

该方法在我的Util课程中已经存在很多年了,它使代码更清晰:

String s = firstNonNull(str1, str2, str3).orElse(str4);

您甚至可以使其通用:

@SafeVarargs
public static <T> Optional<T> firstNonNull(T... objects) {
    return Arrays.stream(objects)
            .filter(Objects::nonNull)
            .findFirst();
}

// Use
Student student = firstNonNull(student1, student2, student3).orElseGet(Student::new);

10
FWIW,在SQL中,此函数称为Coalesce,因此我在代码中也将其称为。是否适合您取决于您​​对SQL的真正需求。
汤姆·安德森

5
如果要将其放入实用程序方法中,则可能会使其变得高效。
Todd Sewell

1
@ToddSewell,你是什么意思?
古斯塔沃·席尔瓦

5
@GustavoSilva Todd可能意味着,这是我的一种实用方法,Arrays.stream()当我可以对a for和a 进行相同操作时,使用它没有任何意义!= null,这效率更高。托德是对的。但是,当我对这种方法进行编码时,我正在寻找一种使用Java 8功能(如OP)进行操作的方法,就是这样。
沃伦

4
@GustavoSilva是的,基本上就是这样:如果要使用util函数,那么对干净代码的担忧不再那么重要了,因此您最好使用更快的版本。
托德·塞韦尔

13

我使用辅助功能,例如

T firstNonNull<T>(T v0, T... vs) {
  if(v0 != null)
    return v0;
  for(T x : vs) {
    if (x != null) 
      return x;
  }
  return null;
}

然后这种代码可以写成

String s = firstNonNull(str1, str2, str3, str4);

1
为什么需要额外的v0参数?
tobias_k

1
@tobias_k使用varargs时,额外的参数是Java中惯用的方式,要求1个或多个参数,而不是0或多个。(请参见Effective Java,Ed。3中的第53项,它min作为示例。)我不太相信这在这里是适当的。
尼克,

3
@尼克是的,我猜了很多,但是如果没有它,该函数也可以正常工作(实际上行为完全相同)。
tobias_k

1
传递额外的第一个参数的关键原因之一是要明确传递数组时的行为。在这种情况下,firstNonNull(arr)如果不为null ,我想返回arr。如果是firstNonNull(T... vs),它将返回arr中的第一个非空条目。
迈克尔·安德森

4

可以应用于任意数量的元素的解决方案可以是:

Stream.of(str1, str2, str3, str4)
      .filter(Object::nonNull)
      .findFirst()
      .orElseThrow(IllegalArgumentException::new)

您可以想象下面的解决方案,但是第一个解决方案可以确保non nullity所有要素

Stream.of(str1, str2, str3).....orElse(str4)

6
orElse尽管str4实际上为空
Naman

1
@nullpointer Or orElseNull,如果str4为null 则等于。
tobias_k

3

您还可以将所有String汇总到String数组中,然后进行for循环以检查分配后的循环并从循环中断开。假设s1,s2,s3,s4均为字符串。

String[] arrayOfStrings = {s1, s2, s3};


s = s4;

for (String value : arrayOfStrings) {
    if (value != null) { 
        s = value;
        break;
    }
}

编辑为在未分配条件的情况下默认抛出条件为s4。


您已经省略了s4(或str4s,即使它为null,也应该最后分配给它。
displayName

3

基于方法且简单。

String getNonNull(String def, String ...strings) {
    for(int i=0; i<strings.length; i++)
        if(strings[i] != null)
             return s[i];
    return def;
}

并将其用作:

String s = getNonNull(str4, str1, str2, str3);

使用数组很简单,而且看起来很漂亮。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.