web-dev-qa-db-ja.com

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

なぜこれが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

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

編集:orElse()をより曖昧に更新しました。

73
neverendingqs

この理由は、戻り値でのOptional<T>の使用です。 Optionalにはnullを含めることはできません。基本的に、「存在しない」と「存在するが、null」に設定されている状況を区別する方法はありません。

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

スロー:

NullPointerException-選択した要素がnullの場合

60
dasblinkenlight

既に説明した のように、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"になります。

40
Holger

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

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

limit()は、reduceに到達する要素を1つだけ許可します。 BinaryOperatorに渡されるreduceは、その1要素を返します。reduceに到達する要素がない場合は"StringWhenListIsEmpty"を返します。

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

11
Nathan

Java.util.Objects.nonNullを使用して、検索する前にリストをフィルタリングできます

何かのようなもの

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

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

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

Optionalが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
ZhongYu