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

fro*_*die 143 java coalesce

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

例如

Double a = null;
Double b = 4.4;
Double c = null;
Run Code Online (Sandbox Code Playgroud)

我想无论如何都将返回的第一个非NULL值的语句a,b以及c-在这种情况下,它会返回b,或4.4.(类似于sql方法 - 返回COALESCE(a,b,c)).我知道我可以用以下方式明确地做到:

return a != null ? a : (b != null ? b : c)
Run Code Online (Sandbox Code Playgroud)

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

And*_*lis 171

Apache Commons Lang 3

ObjectUtils.firstNonNull(T...)

Java 8 Stream

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

  • 我正在寻找相同的字符串,并发现有 SpringUtils.firstNonBlank(T...) 和 SpringUtils.firstNonBlank(T...) 方法。 (3认同)
  • 请注意,此处列出的两种方法都会由于额外的对象分配而引入性能影响,这与简单的三元运算符或 Guava 的 MoreObjects.firstNonNull 不同。 (2认同)

les*_*es2 102

不,没有.

你最接近的是:

public static <T> T coalesce(T ...items) {
    for(T i : items) if(i != null) return i;
    return null;
}
Run Code Online (Sandbox Code Playgroud)

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

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 ...
}
Run Code Online (Sandbox Code Playgroud)

  • 注意:在`coalesce(a,b)`中,如果`b`是复数表达式而`a`不是'null`,则仍然会评估`b`.对于?:条件运算符不是这种情况.见[这个答案](http://stackoverflow.com/a/978357/1402846). (9认同)
  • 我仍然会把它拉成一个私有的帮助器方法,而不是在代码中留下一个"可怕的"条件块 - "这是做什么的?" 这样,如果您确实需要再次使用它,可以使用IDE中的重构工具将方法移动到实用程序类.拥有命名方法有助于记录代码的意图,这总是一件好事,IMO.(并且非var-args版本的开销可能几乎不可测量.) (8认同)
  • 我上面提到的效率原因是每次调用该方法的var arg版本时都会发生数组分配。这对于满手的物品可能是浪费的,我怀疑这是常见用法。 (3认同)

Dav*_*ave 57

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

  • Objects.firstNonNull只接受两个参数; 在番石榴中没有相当的变种.此外,如果两个args都为null,它会抛出NullPointerException - 这可能是也可能不是. (47认同)
  • 该方法现已弃用,建议的替代方法是[MoreObjects.firstNonNull](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/MoreObjects.html#firstNonNull(T ,%20T)) (4认同)
  • 很好的评论,杰克。此 NullPointerException 通常会限制 Objects.firstNonNull 的使用。但是,这是 Guava 完全避免空值的方法。 (2认同)

Chr*_*oom 46

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

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)


Eri*_*ric 24

继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);
}
Run Code Online (Sandbox Code Playgroud)

  • +1漂亮.不确定简单循环的效率优势,但是如果你要以这种方式勉强实现任何微小的效率,它可能也很漂亮. (5认同)
  • 这种方式使得编写重载变体的痛苦更少,更不容易出错! (3认同)
  • 有效版本的要点是不要浪费使用`varargs`分配数组的内存.在这里,你通过为每个嵌套的`coalesce()`调用创建一个堆栈帧来浪费内存.调用`coalesce(a,b,c,d,e)`可以创建最多3个堆栈帧来计算. (2认同)

Fra*_*nta 9

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

有些功能就是这样

public static <T> T coalesce(T ...items) …
Run Code Online (Sandbox Code Playgroud)

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

a != null ? a : (b != null ? b : c)
Run Code Online (Sandbox Code Playgroud)

更新2014-09-02:

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

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


小智 9

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


Jam*_*mol 5

使用番石榴,您可以:

Optional.fromNullable(a).or(b);
Run Code Online (Sandbox Code Playgroud)

它不会抛出NPE如果两个abnull.

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

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();
Run Code Online (Sandbox Code Playgroud)


Luc*_*eón 5

你可以试试这个:

public static <T> T coalesce(T... t) {
    return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);
}
Run Code Online (Sandbox Code Playgroud)

基于回复


小智 5

当您想避免评估某些昂贵的方法时,使用供应商怎么样?

像这样:

public static <T> T coalesce(Supplier<T>... items) {
for (Supplier<T> item : items) {
    T value = item.get();
    if (value != null) {
        return value;
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)
Run Code Online (Sandbox Code Playgroud)

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

此外,您还可以使用流,如下所示:

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}
Run Code Online (Sandbox Code Playgroud)