web-dev-qa-db-ja.com

null値のjdk8ストリームを管理する方法

仲間のJava開発者の皆さん、

JDK8はまだリリースされていないので(とにかく今のところではありません)、主題は少しin advanceかもしれませんが、Lambda式に関する記事、特に既知の新しいコレクションAPIに関連する部分を読んでいましたストリームとして。

Java Magazineの記事 (カワウソの集団アルゴリズムです。)で与えられている例は次のとおりです。

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

私の質問は、Setの内部反復の途中で、カワウソの1つがヌルの場合に何が起こるかということです。

NullPointerExceptionがスローされることを期待しますが、おそらく以前の開発パラダイム(機能しない)にとどまっている可能性があります。これを処理する方法について誰かに教えてもらえますか?

これが本当にNullPointerExceptionをスローする場合、この機能は非常に危険であることがわかり、以下のようにのみ使用する必要があります。

  • 開発者はnull値がないことを確認します(以前の.filter(o-> o!= nullを使用している可能性があります))
  • 開発者は、アプリケーションがnull otterまたは処理する特別なNullOtterオブジェクトを決して生成しないようにします。

最良のオプション、または他のオプションは何ですか?

ありがとう!

77
clement

現在の考え方は、nullを「許容」すること、つまり、一般にnullを許可することのように思われますが、一部の操作は許容度が低く、最終的にNPEをスローする場合があります。 Lambda Librariesエキスパートグループメーリングリストの discussion of nulls 、特に this message 。その後、オプション#3についてのコンセンサスが浮上しました(Doug Leaからの顕著な異議があります)。はい、そうです、NPEで爆発するパイプラインに関するOPの懸念は有効です。

Tony Hoareがnullを "Billion Dollar Mistake。" と呼んだのは何の役にも立たない。nullの扱いは本当の痛みである。クラシックコレクション(ラムダやストリームを考慮しない)であっても、nullには問題があります。 fge がコメントで言及しているように、コレクションによってはnullを許可するものと許可しないものがあります。 nullを許可するコレクションでは、これによりAPIにあいまいさが生じます。たとえば、 Map.get() の場合、nullの戻り値は、キーが存在しその値がnullであるか、キーが存在しないことを示します。 。これらのケースを明確にするために、追加の作業を行う必要があります。

Nullの通常の使用法は、値がないことを示すことです。 Java SE 8で提案されているこれに対処するアプローチは、新しい Java.util.Optional タイプを導入することです。値、デフォルト値を提供する動作、例外をスローする動作、または値が存在しない場合は関数を呼び出す動作など。 Optionalは新しいAPIでのみ使用されますが、システム内の他のすべては依然としてnullの可能性に耐える必要があります。

私のアドバイスは、可能な限り実際のnull参照を避けることです。 「ヌル」のカワウソがどのように存在する可能性があるかを与えられた例から見るのは難しいです。ただし、必要な場合は、ヌル値をフィルタリングするか、センチネルオブジェクトにマッピングするというOPの提案( ヌルオブジェクトパターン )は素晴らしいアプローチです。

39
Stuart Marks

回答は100%正しいですが、 オプション を使用して、リスト自体のnullケース処理を改善するための小さな提案です。

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)部分により、listOfStuffがnullの場合をうまく処理し、NullPointerExceptionで失敗する代わりにemptyListを返すことができます。

68
Johnny

スチュアートの答えは素晴らしい説明を提供しますが、別の例を提供したいと思います。

Null値を含むStreamでreduceを実行しようとしたときにこの問題に遭遇しました(実際にはLongStream.average()で、これは削減の一種です)。 average()はOptionalDoubleを返すため、Streamにnullが含まれる可能性があると想定しましたが、代わりにNullPointerExceptionがスローされました。これは、 Stuartの説明 null v。emptyによるものです。

そのため、OPが示唆するように、次のようなフィルターを追加しました。

list.stream()
    .filter(o -> o != null)
    .reduce(..);

または、以下に示すタンジェントのように、Java AP​​Iによって提供される述語を使用します。

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

Stuartがリンクしたメーリングリストの議論から: StreamsのnullのBrian Goetz

64
bparry

ストリームからヌル値をフィルタリングするだけの場合は、単純に Java.util.Objects.nonNull(Object) へのメソッド参照を使用できます。そのドキュメントから:

このメソッドは、 述語filter(Objects::nonNull)として使用するために存在します

例えば:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

これは印刷されます:

Foo
Bar
18
Andy Thomas

Nullを避ける方法の例groupingByの前にフィルターを使用する

GroupingByの前にnullインスタンスを除外します。

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
7
Ilan M