Java 8での複数のnullチェック


99

複数のnullチェックに対して少し醜い以下のコードがあります。

String s = null;

if (str1 != null) {
    s = str1;
} else if (str2 != null) {
    s = str2;
} else if (str3 != null) {
    s = str3;
} else {
    s = str4;
}

だからOptional.ofNullable、以下のように使ってみましたが、誰かが私のコードを読んでいるかどうかはまだわかりません。Java 8でそれを行うための最良のアプローチは何ですか?

String s = Optional.ofNullable(str1)
                   .orElse(Optional.ofNullable(str2)
                                   .orElse(Optional.ofNullable(str3)
                                                   .orElse(str4)));

Java 9ではで使用できますOptional.ofNullableOR、Java8では他のアプローチはありますか?


4
Java9のor構文 String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);は、Stream.of私が説明するほどよくありません。
ナマン

2
@ OleV.V。何も問題はありません。OPはすでにそれを認識しており、Java-8に固有の何かを求めています。
ナマン

3
ユーザーがJava-8固有のソリューションを求めていることは知っていますが、一般的な注意として、私は一緒に行きますStringUtils.firstNonBlank()
Mohamed Anees A

3
問題は、Java 8 /ストリームがこれに対する最良の解決策ではないということです。そのコードはリファクタリングのように本当ににおいがしますが、より多くのコンテキストなしではそれを伝えるのは本当に難しいです。手始めに-おそらくコレクションにまだないほど密接に関連している3つのオブジェクトがないのはなぜですか?
ビルK

2
@MohamedAneesAは(コメントとして)最良の回答を提供しますが、この場合はStringUtilsのソースを指定しませんでした。とにかく、これらを別々の文字列の束として持つ必要がある場合は、「firstNonBlank」のようなvargsメソッドとしてコード化するのが理想的です。内部の構文は配列であり、単純なfor-eachループを作成して、非null値は自明で明白です。この場合、java 8ストリームは魅力的な迷惑です。彼らはあなたをインライン化し、単純なメソッド/ループであるべきものを複雑にしようと誘惑します。
ビルK

回答:


172

あなたはそうすることができます:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);

16
この。持っているものではなく、必要なものを考えてください。
するThorbjörnRavnアンデルセン

21
速度オーバーヘッドはどのくらいですか?Streamオブジェクトの作成、4つのメソッドの呼び出し、一時配列の作成({str1, str2, str3})は、Javaランタイムが最適化できるローカルのiforまたはよりもはるかに遅いように見え?:ます。ストリーム固有の最適化javacとJavaランタイムにそれと同じくらい速くなるものはあり?:ますか?そうでない場合は、パフォーマンスが重要なコードでこのソリューションをお勧めしません。
PTS

16
@ptsこれは?:コードよりも遅くなる可能性が非常に高いため、パフォーマンスが重要なコードではこれを回避する必要があります。しかし、それははるかに読みやすいので、パフォーマンスに重要ではないコードIMOで推奨する必要があります。これにより、コードの99%以上が確実に作成されます。
アーロン

7
@pts javacランタイム内にもランタイム内にも、ストリーム固有の最適化はありません。これは、すべてのコードのインライン化などの一般的な最適化を排除せず、冗長な操作を排除します。原則として、最終結果は単純な条件式と同じくらい効率的ですが、ランタイムが最もホットなコードパスのみに必要な労力を費やすため、これが達成される可能性はかなり低くなります。
ホルガー

22
素晴らしい解決策!読みやすさを少し向上させるために、最後str4にのパラメーターに追加して使用することをお勧めします。Stream.of(...)orElse(null)
danielp

73

三項条件演算子はどうですか?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;

50
実際、彼は「Java8でこれを行うための最良のアプローチ」を探しています。このアプローチはJava8で使用できるため、OPが「最高」とは何を意味するか、そして何に基づいてOPがより優れているかを決定する根拠に依存します。
Stultuske、

17
私は通常、入れ子になった三項を嫌いますが、これはかなりきれいに見えます。
JollyJoker

11
これは、ネストされた3項演算子を毎日読んでいない人にとっては、解析が非常に困難です。
キュービック

2
@Cubic:解析が難しいと思う場合は、のようなコメントを書いてください// take first non-null of str1..3。それが何をするかを知ったら、それがどのように見えるかが簡単になります。
Peter Cordes

4
@Cubicただし、これは単純な繰り返しパターンです。2行目を解析すると、含まれるケースの数に関係なく、全体を解析しました。そして、完了したら、10の異なるケースの同様の選択を簡潔かつ比較的単純な方法で表現する方法を学びました。?:はしごが次に表示されるときは、はしごの機能がわかります。(ところで、関数型プログラマーはこれを(cond ...)節として認識しています。これは常にガードであり、ガードがtrueの場合に使用する適切な値が続きます。)
cmaster-モニカを

35

ループを使用することもできます。

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}

27

現在の答えはいいですが、あなたは本当にそれをユーティリティメソッドに入れるべきです:

public static Optional<String> firstNonNull(String... strings) {
    return Arrays.stream(strings)
            .filter(Objects::nonNull)
            .findFirst();
}

そのメソッドはUtil何年もの間私のクラスにあり、コードをよりきれいにします:

String s = firstNonNull(str1, str2, str3).orElse(str4);

あなたはそれをジェネリックにすることさえできます:

@SafeVarargs
public static <T> Optional<T> firstNonNull(T... objects) {
    return Arrays.stream(objects)
            .filter(Objects::nonNull)
            .findFirst();
}

// Use
Student student = firstNonNull(student1, student2, student3).orElseGet(Student::new);

10
FWIW、SQLでは、この関数は合体と呼ばれるので、コードでもそれを呼び出します。それがあなたのために働くかどうかは、本当にあなたがSQLをどれだけ好きかによって異なります。
トムアンダーソン

5
ユーティリティメソッドに配置する場合は、効率的にすることもできます。
トッドSewell

1
@ToddSewell、どういう意味?
Gustavo Silva

5
@GustavoSilvaトッドは、おそらくそれを意味し、これは私のユーティリティメソッドであること、使用するにはポイントがないArrays.stream()私が同じことを行うことができるときfor!= null、より効率的であるが、。そしてトッドは正しいでしょう。ただし、このメソッドをコーディングしたとき、OPのようにJava 8の機能を使用してこれを行う方法を探していたので、それがありました。
walen

4
@GustavoSilvaええ、それは基本的にそれです:これを置くつもりの場合、これはユーティリティ関数です。クリーンなコードに関する懸念はもうそれほど重要ではないので、より高速なバージョンを使用することもできます。
トッドSewell

13

私はヘルパー関数を使用しています

T firstNonNull<T>(T v0, T... vs) {
  if(v0 != null)
    return v0;
  for(T x : vs) {
    if (x != null) 
      return x;
  }
  return null;
}

次に、この種のコードは次のように書くことができます

String s = firstNonNull(str1, str2, str3, str4);

1
なぜ余分なv0パラメーターなのですか?
tobias_k

1
@tobias_k varargsを使用する場合の追加のパラメーターは、Javaで0以上ではなく1以上の引数を要求する慣用的な方法です。(min例として使用するEffective Java Ed。3.の項目53を参照してください。)これがここでは適切であると私は確信していません。
Nick

3
@Nickはい、私は多くのことを推測しましたが、関数はそれなしでも同様に機能します(実際にはまったく同じように動作します)。
tobias_k

1
追加の最初の引数を渡す主な理由の1つは、配列を渡すときの動作を明示することです。この場合、firstNonNull(arr)arrがnullでない場合はそれを返します。もしそうなら、firstNonNull(T... vs)代わりにarrに最初のnullでないエントリを返します。
マイケルアンダーソン、

4

必要な数の要素に適用できるソリューションは次のとおりです。

Stream.of(str1, str2, str3, str4)
      .filter(Object::nonNull)
      .findFirst()
      .orElseThrow(IllegalArgumentException::new)

以下のようなソリューションを想像できますが、最初のソリューションnon nullityはすべての要素を保証します

Stream.of(str1, str2, str3).....orElse(str4)

6
str4実際にはnullであるにもかかわらず、orElse
Naman

1
@nullpointer Or orElseNull。これstr4はnullの場合と同じになります。
tobias_k

3

また、すべての文字列を文字列の配列にまとめ、forループを実行して、割り当てられたループをチェックしてループから抜けることもできます。s1、s2、s3、s4がすべて文字列であると仮定します。

String[] arrayOfStrings = {s1, s2, s3};


s = s4;

for (String value : arrayOfStrings) {
    if (value != null) { 
        s = value;
        break;
    }
}

何も割り当てられていない場合、デフォルトで条件をs4にスローするように編集されました。


nullであっても、最後に割り当てられることになっているs4(またはstr4)を省略していsます。
displayName

3

メソッドベースでシンプル。

String getNonNull(String def, String ...strings) {
    for(int i=0; i<strings.length; i++)
        if(strings[i] != null)
             return s[i];
    return def;
}

そしてそれを次のように使用します:

String s = getNonNull(str4, str1, str2, str3);

配列を使用するのは簡単で、見た目もきれいです。


0

Apache Commons Lang 3を使用する場合は、次のように記述できます。

String s = ObjectUtils.firstNonNull(str1, str2, str3, str4);

の使用はObjectUtils.firstNonNull(T...)、この回答から取られまし。関連する質問では、さまざまなアプローチも提示されました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.