如何在 Java 中获取第一个非空值?

Posted

技术标签:

【中文标题】如何在 Java 中获取第一个非空值?【英文标题】:How to get the first non-null value in Java? 【发布时间】:2011-02-15 14:51:23 【问题描述】:

是否存在与 SQL 的 COALESCE 函数等效的 Java?也就是有没有办法返回几个变量的第一个非空值?

例如

Double a = null;
Double b = 4.4;
Double c = null;

我想以某种方式返回abc 的第一个非空值的语句——在这种情况下,它将返回b 或4.4。 (类似于 sql 方法 - 返回 COALESCE(a,b,c))。我知道我可以通过以下方式明确地做到这一点:

return a != null ? a : (b != null ? b : c)

但我想知道是否有任何内置的、可接受的函数来完成此操作。

【问题讨论】:

你不应该需要这样的函数,因为如果 'b' 有你想要的答案,你通常不会计算 'c'。也就是说,你不会为了保留一个而建立一个可能的答案列表。 警告:并非所有 RDBMS 在 COALESCE 上都短路。 Oracle 最近才开始这样做。 @BrainSlugs83 认真的吗? Java应该? 【参考方案1】:

Apache Commons Lang 3

ObjectUtils.firstNonNull(T...)

Java 8 流

Stream.of(T...).filter(Objects::nonNull).findFirst().orElse(null)

【讨论】:

我一直在寻找相同的字符串,但发现有 SpringUtils.firstNonBlank(T...) 和 SpringUtils.firstNonBlank(T...) 方法。 请注意,这里列出的两种方法都会由于额外的对象分配而引入性能影响,这与简单的三元运算符或 Guava 的 MoreObjects.firstNonNull 不同。 Stream.of(null,"a") 将不起作用,因为 of 函数使用 @NonNull 注释【参考方案2】:

不,没有。

你能得到的最接近的是:

public static <T> T coalesce(T ...items) 
    for(T i : items) if(i != null) return i;
    return null;

出于高效的原因,您可以按如下方式处理常见情况:

public static <T> T coalesce(T a, T b) 
    return a == null ? b : a;

public static <T> T coalesce(T a, T b, T c) 
    return a != null ? a : (b != null ? b : c);

public static <T> T coalesce(T a, T b, T c, T d) 
    return ...

【讨论】:

我上面提到的效率原因是每次调用方法的 var arg 版本时都会发生数组分配。这可能会浪费大量物品,我怀疑这将是常见的用法。 酷。谢谢。在这种情况下,我可能会坚持使用嵌套条件运算符,因为这是唯一一次必须使用它,并且用户定义的方法将是矫枉过正... 我仍然会把它拉到一个私有的辅助方法中,而不是在代码中留下一个“看起来很吓人”的条件块——“这是做什么的?”这样,如果您确实需要再次使用它,您可以使用 IDE 中的重构工具将该方法移动到实用程序类。拥有命名方法有助于记录代码的意图,这总是一件好事,IMO。 (而且非 var-args 版本的开销可能几乎无法衡量。) 注意:在coalesce(a, b) 中,如果b 是一个复杂的表达式并且a 不是nullb 仍然会被计算。 ?: 条件运算符不是这种情况。见this answer。 这要求在调用合并之前预先计算每个参数,出于性能原因毫无意义【参考方案3】:

如果只有两个变量要检查并且您使用的是 Guava,则可以使用 MoreObjects.firstNonNull(T first, T second)。

【讨论】:

Objects.firstNonNull 只接受两个参数; Guava 中没有等效的可变参数。此外,如果两个参数都为空,它会抛出 NullPointerException - 这可能是可取的,也可能不是可取的。 好评论,杰克。此 NullPointerException 通常会限制 Objects.firstNonNull 的使用。然而,这是 Guava 完全避免空值的方法。 该方法现已弃用,推荐的替代方法是MoreObjects.firstNonNull 如果不需要 NPE,请参阅 this answer【参考方案4】:

如果只有两个引用要测试并且您使用的是 Java 8,则可以使用

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p

如果你 import static Optional 表达式还不错。

不幸的是,使用 Optional 方法无法实现“多个变量”的情况。相反,您可以使用:

Object o = null;
Object p = null;
Object q = "p";

Optional<Object> r = Stream.of( o, p, q ).filter( Objects::nonNull ).findFirst();
System.out.println( r.orElse(null) );   // p

【讨论】:

可以不使用可选:Object default = "some value"; Object res = ((res = getSomeNullable()) != null) ? res : default; 【参考方案5】:

根据 LES2 的回答,您可以通过调用重载函数来消除高效版本中的一些重复:

public static <T> T coalesce(T a, T b) 
    return a != null ? a : b;

public static <T> T coalesce(T a, T b, T c) 
    return a != null ? a : coalesce(b,c);

public static <T> T coalesce(T a, T b, T c, T d) 
    return a != null ? a : coalesce(b,c,d);

public static <T> T coalesce(T a, T b, T c, T d, T e) 
    return a != null ? a : coalesce(b,c,d,e);

【讨论】:

漂亮+1。不确定与简单循环相比的效率优势,但如果您打算以这种方式勉强获得任何微小的效率,它可能还不错。 这种方式可以减少编写重载变体的痛苦和错误! 高效版本的要点是不要浪费内存使用varargs分配数组。在这里,您通过为每个嵌套的coalesce() 调用创建一个堆栈帧来浪费内存。调用 coalesce(a, b, c, d, e) 最多可创建 3 个堆栈帧进行计算。【参考方案6】:

这种情况需要一些预处理器。因为如果您编写一个选择第一个非空值的函数(静态方法),它会评估所有项目。如果某些项目是方法调用(可能是耗时的方法调用),那就有问题了。即使它们之前的任何项目不为空,也会调用此方法。

这样的一些功能

public static <T> T coalesce(T ...items) …

应该使用,但在编译成字节码之前应该有一个预处理器,它可以找到这个“coalesce function”的用法并用类似的结构替换它

a != null ? a : (b != null ? b : c)

2014 年 9 月 2 日更新:

感谢 Java 8 和 Lambdas,有可能在 Java 中实现真正的合并!包括关键特性:仅在需要时才评估特定表达式 - 如果前面的表达式不为空,则不评估后面的表达式(不调用方法,不进行计算或磁盘/网络操作)。

我写了一篇关于它的文章Java 8: coalesce – hledáme neNULLové hodnoty——(用捷克语写的,但我希望代码示例对每个人都可以理解)。

【讨论】:

不错的文章 - 不过要是能有英文就好了。 关于该博客页面的某些内容无法与 Google 翻译一起使用。 :-(【参考方案7】:

使用番石榴,您可以做到:

Optional.fromNullable(a).or(b);

如果ab 都是null,则不会抛出NPE。

编辑:我错了,它确实抛出了 NPE。 Michal Čizmazia 评论的正确方法是:

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();

【讨论】:

嘿,确实如此:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null) 这可以解决问题:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()【参考方案8】:

你可以试试这个:

public static <T> T coalesce(T... t) 
    return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);

基于this 响应

【讨论】:

【参考方案9】:

为了完整起见,“几个变量”的情况确实是可能的,尽管一点也不优雅。例如,对于变量opq

Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )

请注意orElseGet() 的使用注意opq 不是变量而是表达式昂贵或具有不良副作用的情况。

在最一般的情况下coalesce(e[1],e[2],e[3],...,e[N])

coalesce-expression(i) ==  e[i]  when i = N
coalesce-expression(i) ==  Optional.ofNullable( e[i] ).orElseGet(()-> coalesce-expression(i+1) )  when i < N

这会生成过长的表达式。然而,如果我们试图转移到一个没有null 的世界,那么v[i] 很可能已经是Optional&lt;String&gt; 类型,而不是简单的String。在这种情况下,

result= o.orElse(p.orElse(q.get())) ;

或者在表达式的情况下:

result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;

此外,如果您也转向函数式声明式样式,opq 应该是 Supplier&lt;String&gt; 类型,例如:

Supplier<String> q= ()-> q-expr ;
Supplier<String> p= ()-> Optional.ofNullable(p-expr).orElseGet( q ) ;
Supplier<String> o= ()-> Optional.ofNullable(o-expr).orElseGet( p ) ;

然后整个coalesce 简化为o.get()

举个更具体的例子:

Supplier<Integer> hardcodedDefaultAge= ()-> 99 ;
Supplier<Integer> defaultAge= ()-> defaultAgeFromDatabase().orElseGet( hardcodedDefaultAge ) ;
Supplier<Integer> ageInStore= ()-> ageFromDatabase(memberId).orElseGet( defaultAge ) ;
Supplier<Integer> effectiveAge= ()-> ageFromInput().orElseGet( ageInStore ) ;

defaultAgeFromDatabase()ageFromDatabase()ageFromInput() 已经自然而然地返回了 Optional&lt;Integer&gt;

然后coalesce 变为effectiveAge.get() 或简单的effectiveAge,如果我们对Supplier&lt;Integer&gt; 感到满意的话。

恕我直言,随着 Java 8 的出现,我们将看到越来越多的代码结构是这样的,因为它同时非常易于解释和高效,尤其是在更复杂的情况下。

我确实错过了一个类Lazy&lt;T&gt;,它只调用一次Supplier&lt;T&gt;,但很懒惰,以及Optional&lt;T&gt;定义的一致性(即Optional&lt;T&gt;-Optional&lt;T&gt;运算符,甚至@987654358 @)。

【讨论】:

【参考方案10】:

当您想避免评估一些昂贵的方法时,如何使用供应商?

像这样:

public static <T> T coalesce(Supplier<T>... items) 
for (Supplier<T> item : items) 
    T value = item.get();
    if (value != null) 
        return value;
    
    return null;

然后像这样使用它:

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)

您还可以对带有两个、三个或四个参数的调用使用重载方法。

此外,您还可以通过以下方式使用流:

public static <T> T coalesce2(Supplier<T>... s) 
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);

【讨论】:

如果第一个参数会被检查,为什么还要将其包装在Supplier 中?为了统一? 这可能有点旧,但为了完整起见:@Triqui 的方法不一定评估传递给 coalesce 的所有参数 - 这就是参数类型为 @987654326 @。只有在调用get() 时才会评估它们 - 如果第一个参数符合条件,则不需要评估其余参数。【参考方案11】:

怎么样:

firstNonNull = FluentIterable.from(
    Lists.newArrayList( a, b, c, ... ) )
        .firstMatch( Predicates.notNull() )
            .or( someKnownNonNullDefault );

Java ArrayList 方便地允许空条目,并且无论要考虑的对象数量如何,此表达式都是一致的。 (在这种形式中,所有考虑的对象都必须是同一类型。)

【讨论】:

【参考方案12】:

从 Java 9 开始,有内置的 Objects.requireNonNullElse 方法用于两个参数合并。这对我来说是最有用的。

【讨论】:

【参考方案13】:
Object coalesce(Object... objects)

    for(Object o : object)
        if(o != null)
            return o;
    return null;

【讨论】:

天哪,我讨厌泛型。我马上就明白了你的意思。我不得不看@LES2 两次才能确定他在做同样的事情(而且可能“更好”)!为清楚起见 +1 是的,泛型是要走的路。但我对这些错综复杂的东西并不熟悉。 是时候学习泛型了 :-)。 @LES2 的示例与此之间几乎没有区别,除了 T 而不是 Object。 -1 用于构建一个函数,该函数将强制将返回值转换回 Double。也用于以全大写命名 Java 方法,这在 SQL 中可能很好,但在 Java 中不是很好的风格。 我意识到全大写是不好的做法。我只是向 OP 展示如何以他们要求的名称编写函数。同意,回​​到Double 的演员阵容远非理想。我只是不知道静态函数可以被赋予类型参数。我以为只是上课。

以上是关于如何在 Java 中获取第一个非空值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PySpark 中用该列的第一个非空值填充该列的空值

如何从c#中的数组中获取所有非空值[重复]

SQL Server 2008 R2:从列中获取第一个非空值

Mysql - 转换非空值第 2 部分

SQL 查询以填补跨时间缺失的空白并获取最后一个非空值

如何使用 php 从 mySQL DB 中的两个表中添加非空值?