关于coalesce:如何在Java中获取第一个非null值?

How to get the first non-null value in Java?

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

例如

1
2
3
Double a = null;
Double b = 4.4;
Double c = null;

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

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

但我想知道是否有任何内置的,可接受的功能来实现这一目标。


Apache Commons Lang 3

ObjectUtils.firstNonNull(T...)

Java 8 Stream

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


不,没有。

你最接近的是:

1
2
3
4
public static < T > T coalesce(T ...items) {
    for(T i : items) if(i != null) return i;
    return null;
}

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

1
2
3
4
5
6
7
8
9
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 ...
}


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


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

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

如果导入静态可选,表达式也不错。

不幸的是,使用Optional-method无法使用"几个变量"。相反,你可以使用:

1
2
3
4
5
6
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

继LES2的答案之后,您可以通过调用重载函数来消除高效版本中的一些重复:

1
2
3
4
5
6
7
8
9
10
11
12
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);
}


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

有些功能就是这样

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

应该使用但在编译成字节代码之前应该有一个预处理器,它可以找到这个?coalesce函数的用法"并用类似的构造替换它

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

更新2014-09-02:

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

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


使用番石榴,您可以:

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

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

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

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


仅仅为了完整性,"几个变量"的情况确实是可能的,尽管根本不是优雅的。例如,对于变量opq

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

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

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

1
2
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。在这种情况下,

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

或者在表达式的情况下:

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

此外,如果您还要转换为函数声明式样式,opq的类型应为Supplier,如下所示:

1
2
3
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()

有关更具体的示例:

1
2
3
4
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

如果我们对Supplier感到满意,那么coalesce会变成effectiveAge.get()或者只是effectiveAge

恕我直言,使用Java 8,我们将看到越来越多的代码结构,因为它同时具有极强的自我解释性和高效性,尤其是在更复杂的情况下。

我确实错过了一个Lazy< T >的类Lazy< T >,只是一次调用Supplier< T >,但懒惰,以及Optional< T >定义的一致性(即Optional< T > - Optional< T >运算符,甚至Supplier>)。


你可以试试这个:

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

基于此回复


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

像这样:

1
2
3
4
5
6
7
8
public static < T > T coalesce(Supplier< T >... items) {
for (Supplier< T > item : items) {
    T value = item.get();
    if (value != null) {
        return value;
    }
    return null;
}

然后像这样使用它:

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

您还可以使用重载方法来调用具有两个,三个或四个参数的调用。

此外,您还可以使用以下类似的流:

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


怎么样:

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

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


1
2
3
4
5
6
7
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}