最初に見つかった要素がnullの場合、findFirst()がNullPointerExceptionをスローするのはなぜですか?


89

なぜこれがスローするのjava.lang.NullPointerExceptionですか?

List<String> strings = new ArrayList<>();
        strings.add(null);
        strings.add("test");

        String firstString = strings.stream()
                .findFirst()      // Exception thrown here
                .orElse("StringWhenListIsEmpty");
                //.orElse(null);  // Changing the `orElse()` to avoid ambiguity

の最初の項目stringsnull、です。これは完全に許容できる値です。さらに、オプションをfindFirst()返します。これは、sを処理できるようにするためにさらに意味があります。findFirst()null

編集:orElse()あいまいさが少なくなるようにを更新しました。


4
nullは完全に許容できる値ではありません...代わりに「」を使用してください
Michele Lacorte 2015

1
@MicheleLacorteStringここで使用していますが、DBの列を表すリストの場合はどうなりますか?その列の最初の行の値はnullです。
neverendingqs 2015

はい。ただし、Javaではnullは受け入れられません。クエリを使用してnullをdbに設定します
Michele Lacorte 2015

10
@MicheleLacortenullは、一般的に言って、Javaで完全に受け入れられる値です。特に、これはの有効な要素ですArrayList<String>。ただし、他の値と同様に、それを使用して実行できることには制限があります。「絶対に使用しないでくださいnull」は避けられないので、役に立つアドバイスではありません。
ジョンボリンジャー2015

@ NathanHughes-電話をかけるときにはfindFirst()、他にやりたいことは何もないと思います。
neverendingqs 2015

回答:


72

この理由Optional<T>は、リターンでの使用です。オプションでを含めることはできませんnull。基本的に、「そこにない」と「あるが、に設定されているnull」という状況を区別する方法はありません。

そのため、ドキュメントでnullは、が選択されている場合の状況を明示的に禁止していますfindFirst()

スロー:

NullPointerException -選択した要素が null


3
のインスタンス内にプライベートブール値を持つ値が存在するかどうかを追跡するのは簡単ですOptional。とにかく、私は暴言を吐き出していると思います-言語がそれをサポートしていない場合、それはそれをサポートしていません。
neverendingqs 2015

2
@neverendingqsもちろん、booleanこれら2つの状況を区別するためにを使用することは完全に理にかなっています。私にOptional<T>は、ここの使用は疑わしい選択だったように見えます。
セルゲイカリニチェンコ2015

1
@neverendingqs独自のnullをロールする以外に、これに代わる見栄えの良い方法は考えられません。これも理想的ではありません。
Sergey Kalinichenko 2015

1
最終的に、任意のIterableタイプからイテレータを取得し、チェックしてhasNext()、適切な値を返すプライベートメソッドを作成しました。
neverendingqs 2015

1
場合、私はそれがより理にかなっていると思うfindFirst戻りPOの場合は空のオプション値
ダニー

47

既に述べた、APIの設計者は、開発者が治療を希望していることを前提としていないnull値と不在の値を同じように。

それでもそれを実行したい場合は、シーケンスを適用して明示的に実行できます

.map(Optional::ofNullable).findFirst().flatMap(Function.identity())

ストリームに。最初の要素がない場合、または最初の要素がnull。の場合、どちらの場合も結果は空のオプションになります。だからあなたの場合、あなたは使うかもしれません

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);

null最初の要素が存在しないかnull。のいずれかである場合に値を取得します。

これらのケースを区別したい場合は、次のflatMap手順を省略できます。

Optional<String> firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
                   firstString.orElse("first element is null"));

これは、更新された質問と大差ありません。あなただけ交換する必要が"no such element""StringWhenListIsEmpty""first element is null"してnull。ただし、条件文が気に入らない場合は、次のようにすることもできます。

String firstString = strings.stream().skip(0)
    .map(Optional::ofNullable).findFirst()
    .orElseGet(()->Optional.of("StringWhenListIsEmpty"))
    .orElse(null);

さて、firstStringとなりますnull要素が存在するがある場合null、それはなります"StringWhenListIsEmpty"どの要素が存在しない場合。


申し訳ありませんが、私の質問はnull、1)最初の要素がであるnullか、2)リスト内に要素が存在しないかのいずれかで戻りたいと示唆している可能性があることに気付きました。あいまいさを取り除くために質問を更新しました。
neverendingqs 2015

1
3番目のコードスニペットでは、Optionalにを割り当てることができますnull。以来Optional、「値型」ことになって、それがnullになることはありません。また、オプションは決して比較されるべきではありません==。Java 10では、コードが失敗する可能性があります:)または、値型がJavaに導入されるたびに。
ZhongYu

1
@ bayou.io:ドキュメントがいることを言っていないの参照値の種類には、することはできませんnullとしながら、インスタンスがで比較すべきではない==、参照がために試験することができるnull使用して==いるのですとしてだけのためにそれをテストする方法nullnullすべてのインスタンス変数と配列要素のデフォルト値がであるため、このような「never 」への移行が既存のコードでどのように機能するかわかりませんnull。スニペットは確かに最良のコードではありませんが、nullsを現在価値として扱うタスクでもありません。
ホルガー2015

ジョンローズを参照してください-また、nullを使用しても、「==」演算子と比較することはできません
ZhongYu 2015

1
このコードはジェネリックAPIを使用しているため、これは概念でボックス表現と呼ばれるものnullです。ただし、このような架空の言語変更により、コンパイラーはここでエラーを発行するため(コードを黙って壊さないでください)、Java10に適合させる必要がある可能性があるという事実に耐えることができStreamます。APIはおそらくその時もかなり異なって見えます…
Holger 2015

18

java.util.Objects.nonNull検索する前にリストをフィルタリングするために使用できます

何かのようなもの

list.stream().filter(Objects::nonNull).findFirst();

1
の最初の項目firstStringがであるnull場合になりたいstringsですnull
neverendingqs 2017年

2
残念ながら、Optional.ofnullセーフではないものを使用します。あなたは可能性mapOptional.ofNullable と、その後使用しfindFirstていますがオプションのオプションで終わるだろう
マトス

14

次のコードはfindFirst()、に置き換えられlimit(1)、次のように置き換えorElse()られreduce()ます。

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);

limit()1つの要素のみがに到達できるようにしますreduce。にBinaryOperator渡されるとreduce、その1つの要素が返されます。または"StringWhenListIsEmpty"、に到達する要素がない場合は返されますreduce

このソリューションOptionalBinaryOperator利点は、割り当てられておらず、ラムダが何も割り当てないことです。


1

オプションは「値」型であると想定されています。(javadocの詳細を読んでください:) JVMは、すべてOptional<Foo>をただFooで置き換えることさえでき、すべてのボックス化およびボックス化解除のコストを取り除きます。AnullはFooは空を意味しますOptional<Foo>

ブールフラグを追加せずに、null値でOptionalを許可することは可能な設計です-センチネルオブジェクトを追加するだけです。(this歩哨として使用することもできます。Throwable.causeを参照してください)

オプションでnullをラップできないという決定は、実行時のコストに基づいていません。これは非常に争われた問題であり、メーリングリストを掘り下げる必要があります。その決定は誰にとっても説得力があるわけではありません。

いずれにせよ、Optionalはnull値をラップできないため、のような場合にはコーナーに押し込まれfindFirstます。彼らは、null値が非常にまれであると推論したに違いありません(Streamはnull値を禁止する必要があるとさえ考えられていました)。したがって、空のストリームではなくnull値に例外をスローする方が便利です。

回避策は、ボックス化することですnull

class Box<T>
    static Box<T> of(T value){ .. }

Optional<Box<String>> first = stream.map(Box::of).findFirst();

(彼らは、すべてのOOP問題の解決策は別のタイプを導入することだと言っています:)


1
別のBoxタイプを作成する必要はありません。Optional型自体は、この目的を果たすことができます。例については私の答えを参照しください。
ホルガー2015

@ Holger-はい、しかしそれはオプションの意図された目的ではないので混乱するかもしれません。OPの場合、nullは他の値と同様に有効な値であり、特別な扱いはありません。(
しばらくしてから
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.