Javaで最初のnullでない値を取得するにはどうすればよいですか?


154

SQLのCOALESCE関数に相当するJavaはありますか?つまり、いくつかの変数の最初のnull以外の値を返す方法はありますか?

例えば

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

私は何とかの最初のnull以外の値を返します声明を持ちたいabと、cこの場合には、それが戻ってくる- b、または4.4。(sqlメソッドのようなもの-return COALESCE(a,b,c))。私はそれを次のようなもので明示的に行うことができることを知っています:

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

しかし、これを実現するための組み込みの受け入れられた機能があるのか​​と思いました。


3
「b」があなたが望む答えを持っているなら「c」を一般的に計算しないので、あなたはこのような関数を必要とすべきではありません。つまり、可能な回答のリストを作成して、それを保持するだけではありません。
Peter Lawrey、

警告:COALESCEですべてのRDBMSが短絡するわけではありません。オラクルは、つい最近、それを始めました。
アダム・ゲント

3
@ BrainSlugs83マジ?Javaは?
ドミトリーギン

回答:


108

いいえ、ありません。

あなたが得ることができる最も近いものは:

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 ...
}

3
上で述べた効率的な理由は、メソッドのvar argバージョンを呼び出すたびに配列の割り当てが発生することです。これは、私が一般的な使用になると思われるアイテムの手いっぱいには無駄になる可能性があります。
les2

涼しい。ありがとう。その場合は、使用する必要があるのはこのときだけであり、ユーザー定義のメソッドが過剰になるため、おそらくネストされた条件演算子にこだわるでしょう
froadie

8
コードに「怖い」条件ブロックを残すのではなく、それをプライベートヘルパーメソッドに引き込みます-「何をするのですか?」そうすれば、再び使用する必要がある場合は、IDEのリファクタリングツールを使用して、メソッドをユーティリティクラスに移動できます。名前付きメソッドがあると、コードの目的を文書化するのに役立ちます。これは常に良いことです、IMO。(そしてvar-args以外のバージョンのオーバーヘッドはおそらくほとんど測定できません。)
les2

10
注意:ではcoalesce(a, b)bが複雑な式で、aがでないnull場合bでも、は評価されます。これは、?:条件演算子には当てはまりません。この回答を参照してください。
パン

これには、合体の呼び出しの前にすべての引数を事前に計算する必要があります


59

チェックする変数が2つだけあり、Guavaを使用している場合は、 MoreObjects.firstNonNull(T first、T second)を


49
Objects.firstNonNullは2つの引数のみを受け取ります。Guavaには同等の可変引数はありません。また、両方の引数がnullの場合は、NullPointerExceptionがスローされます。これが望ましい場合と望ましくない場合があります。

2
良いコメント、ジェイク。このNullPointerExceptionは、Objects.firstNonNullの使用を制限することがよくあります。ただし、ヌルをまったく回避するのはGuavaのアプローチです。
Anton Shchastnyi 2014年

4
そのメソッドは現在廃止されており、推奨される代替はMoreObjects.firstNonNull
davidwebster48

1
NPEが望ましくない場合は、この回答を
OrangeDog

51

テストする参照が2つしかなく、Java 8を使用している場合は、

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

静的オプションをインポートする場合、式は悪くありません。

残念ながら、「いくつかの変数」を使用したケースは、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

23

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);
}

5
かなりの+1。単純なループに対する効率の利点についてはわかりませんが、この方法で小さな効率を実現するのであれば、かなり良いかもしれません。
カールマナスター

3
この方法により、オーバーロードされたバリアントを作成するのがはるかに簡単になり、エラーが発生しにくくなります。
les2

2
効率的なバージョンのポイントは、を使用して配列を割り当てるときにメモリを浪費しないことでしたvarargs。ここでは、ネストされたcoalesce()呼び出しごとにスタックフレームを作成してメモリを浪費しています。呼び出しcoalesce(a, b, c, d, e)により、計算するスタックフレームが3つまで作成されます。
ルーク

10

この状況では、いくつかのプリプロセッサが必要です。なぜなら、最初のnullではない値を選択する関数(静的メソッド)を作成すると、すべての項目が評価されるからです。一部の項目がメソッド呼び出しである場合は問題があります(時間のかかるメソッド呼び出しである可能性があります)。また、このメソッドは、その前のアイテムがnullでない場合でも呼び出されます。

このようないくつかの機能

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

使用する必要がありますが、バイトコードにコンパイルする前に、この「コアレス関数」の使用法を見つけ、それを次のような構造に置き換えるプリプロセッサが必要です

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

アップデート2014-09-02:

Java 8とLambdasのおかげで、Javaで真の融合を実現できる可能性があります。重要な機能を含む:特定の式は必要な場合にのみ評価されます。以前の式がnullでない場合、次の式は評価されません(メソッドは呼び出されず、計算またはディスク/ネットワーク操作は行われません)。

Java 8:合体–hledámeneNULLovéhodnoty – についての記事を書きました(チェコ語で書かれていますが、コード例が誰にとっても理解できることを願っています)。


1
素敵な記事ですが、英語で書いておくといいでしょう。
クォンタム

1
そのブログページには、Google翻訳で動作しないものがあります。:-(
HairOfTheDog 2015年

5

グアバでできること:

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

両方の場合NPEを投げないabされていますnull

編集:私は間違っていました、それはNPEをスローします。MichalČizmaziaがコメントした正しい方法は次のとおりです。

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

1
こんにちは、それはします:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
MichalČizmazia2013

1
これはトリックを実行します:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
ミハルČizmazia13年

4

完全を期すために、「いくつかの変数」のケースは実際には可能ですが、エレガントではありません。たとえば、変数のためにopq

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

使用に注意してくださいorElseGet()ケースに参加しているopと、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

これは、過度に長い式を生成する可能性があります。ただし、のない世界に移動しようとしている場合は、単純にではなくnullv[i]おそらくすでにタイプOptional<String>になっていますString。この場合、

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

または式の場合:

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

さらに、あなたはまた、機能的、宣言型スタイルに移動している場合opおよび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()effectiveAgeSupplier<Integer>

IMHO、Java 8では、特に複雑なケースでは非常に自己説明的で効率的であるため、このような構造のコードがますます多く見られます。

1回だけLazy<T>呼び出すSupplier<T>が、遅延のクラスと、定義の一貫性Optional<T>(つまりOptional<T>- Optional<T>演算子、またはSupplier<Optional<T>>)が欠けているのを見逃しています。


4

あなたはこれを試すことができます:

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

この回答に基づく


3

高価な方法の評価を避けたい場合は、サプライヤを使用してみませんか?

このような:

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)

また、2つ、3つ、または4つの引数を持つ呼び出しにオーバーロードされたメソッドを使用することもできます。

さらに、次のようなストリームを使用することもできます。

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

Supplierとにかく検査される場合、なぜ最初の引数をaでラップするのですか?均一性のために?
Inego

0

どうですか:

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

Java ArrayListはnullエントリを便利に許可し、この式は考慮されるオブジェクトの数に関係なく一貫しています。(この形式では、考慮されるすべてのオブジェクトは同じタイプである必要があります。)


-3
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}

2
神私はジェネリックが嫌いです。あなたの意味がすぐにわかりました。私は@ LES2を2度見て、彼が同じことをしている(そしておそらく「より良い」)ことを理解しなければなりませんでした。明確にするために+1
ビルK

ええ、ジェネリックは行くべき道です。しかし、私は複雑さについてそれほど詳しくありません。
エリック

10
ジェネリックを学ぶ時間:-)。Objectの代わりにTを除いて、@ LESS2の例とこれの違いはほとんどありません。戻り値をDoubleに強制的にキャストする関数を作成する場合は-1。また、Javaメソッドをすべて大文字で命名することもできます。これはSQLでは問題ないかもしれませんが、Javaでは適切なスタイルではありません。
Avi

1
オールキャップスは悪い習慣だと思います。私はちょうど彼らが要求した名前で関数を書く方法をOPに示していました。同意し、キャストバックDoubleは理想からかけ離れています。静的関数に型パラメーターを指定できることを知らなかっただけです。ただの授業だと思いました。
エリック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.