null値のjdk8ストリームをどのように管理する必要がありますか


88

こんにちは仲間のJava開発者、

in advanceJDK8がまだリリースされていないので(そして今のところまだリリースされていないので)、主題が少しかもしれないことは知っていますが、Lambda式、特にStreamとして知られる新しいコレクション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つがnullの場合はどうなるかということです。

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

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

  • null値がないことを確認する開発者(おそらく以前の.filter(o-> o!= null)を使用)
  • 開発者は、アプリケーションがnullカワウソまたは処理する特別なNullOtterオブジェクトを生成しないようにします。

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

ありがとう!


3
ここで正しいことをするのはプログラマー次第だと思います。JVMとコンパイラーはそれだけのことしかできません。ただし、一部のコレクション実装ではnull値が許可されないことに注意してください。
fge 2013年

18
あなたは使うことができfilter(Objects::nonNull)Objectsからjava.utils
Benj

回答:


45

現在の考え方は、nullを「許容」すること、つまり、nullを一般的に許可することであるように思われますが、一部の操作は許容度が低く、最終的にNPEをスローする可能性があります。Lambda Librariesエキスパートグループのメーリングリストのnull説明、特にこのメッセージを参照してください。その後、オプション#3に関するコンセンサスが生まれました(Doug Leaからの顕著な反対がありました)。そうです、パイプラインがNPEで爆発するというOPの懸念は有効です。

トニー・ホーアがヌルを「10億ドルの間違い」と呼んだのは当然のことですヌルに対処することは本当に苦痛です。クラシックコレクション(ラムダやストリームを考慮しない)でも、nullには問題があります。以下のようFGEはコメントで述べたように、いくつかのコレクションがヌル、他にはないことができます。nullを許可するコレクションでは、これによりAPIにあいまいさが生じます。たとえば、Map.get()の場合、nullが返されると、キーが存在し、その値がnullであるか、キーが存在しないことが示されます。これらのケースを明確にするために、追加の作業を行う必要があります。

nullの通常の使用法は、値がないことを示すことです。Java SE 8で提案されているこの問題に対処するためのアプローチjava.util.Optionalは、値の有無をカプセル化する新しいタイプを導入することです。デフォルト値を指定したり、例外をスローしたり、関数を呼び出したりする動作もあります。値はありません。Optionalは新しいAPIによってのみ使用されますが、システム内の他のすべては依然としてnullの可能性に耐える必要があります。

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


3
なぜヌルを避けるのですか?それらはデータベースで広く使用されています。
Raffi Khatchadourian 2015

5
@RaffiKhatchadourianはい、データベースではnullが使用されていますが、同様に問題があります。これこれを見てすべての答えとコメントを読んでください。ブール式に対するSQLnullの影響も考慮してください。en.wikipedia.org / wiki / …...これはクエリエラーの豊富な原因です。
スチュアートマークス

1
@RaffiKhatchadouriannull吸うので、それが理由です。Acc。私が見た(それを見つけることができない)調査では、NPEはJavaで一番の例外です。SQLは高水準プログラミング言語ではなく、確かに機能的ではなく、nullを軽蔑しているため、気にしません。
アビジットサルカール2017

91

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

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

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


2
私はこれが好きで、nullを明示的にチェックすることを避けます。
クリス

1
これは最も明確に見えます..素晴らしく、まさに私が必要としていたもの
Abdullah AlNoman18年

別の方法でのみ、明示的にnullをチェックしています。nullが許可されている場合は、オプションである必要があります。それがアイデアですよね?オプションですべてのnull値を禁止します。さらに、空のリストの代わりにnullを返すことは悪い習慣であるため、これを見ると2つのコードの臭いが表示されます。オプションが使用されていないことと、空のストリームが返されないことです。そして後者は20年以上利用可能であるため、成熟しています...
KoosGadellaa19年

空のリストではなくnullを返したい場合はどうすればよいですか?
アッシュバーンRK

@AshburnRKそれは悪い習慣です。空のリストを返す必要があります。
ジョニー

69

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

reducenull値を含むストリームでを実行しようとしたときにこの問題が発生しました(実際にはLongStream.average()、これは一種の削減です)。average()はを返すのでOptionalDouble、Streamにnullが含まれている可能性があると想定しましたが、代わりにNullPointerExceptionがスローされました。これは、スチュアートがnullv。emptyについて説明したためです。

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

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

または、以下で指摘するタンゲンのように、JavaAPIによって提供される述語を使用します。

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

スチュアートがリンクしたメーリングリストのディスカッションから: Streamsのnullに関するBrian Goetz


19

ストリームからnull値をフィルタリングするだけの場合は、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

7

nullを回避する方法の例(groupingByの前にfilterを使用するなど)

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

これが例です

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.