如何在 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;
我想以某种方式返回a
、b
和c
的第一个非空值的语句——在这种情况下,它将返回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
不是null
,b
仍然会被计算。 ?: 条件运算符不是这种情况。见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);
如果a
和b
都是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】:为了完整起见,“几个变量”的情况确实是可能的,尽管一点也不优雅。例如,对于变量o
、p
和q
:
Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )
请注意orElseGet()
的使用注意o
、p
和q
不是变量而是表达式昂贵或具有不良副作用的情况。
在最一般的情况下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<String>
类型,而不是简单的String
。在这种情况下,
result= o.orElse(p.orElse(q.get())) ;
或者在表达式的情况下:
result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;
此外,如果您也转向函数式声明式样式,o
、p
和 q
应该是 Supplier<String>
类型,例如:
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<Integer>
。
然后coalesce
变为effectiveAge.get()
或简单的effectiveAge
,如果我们对Supplier<Integer>
感到满意的话。
恕我直言,随着 Java 8 的出现,我们将看到越来越多的代码结构是这样的,因为它同时非常易于解释和高效,尤其是在更复杂的情况下。
我确实错过了一个类Lazy<T>
,它只调用一次Supplier<T>
,但很懒惰,以及Optional<T>
定义的一致性(即Optional<T>
-Optional<T>
运算符,甚至@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 中用该列的第一个非空值填充该列的空值