Java8でラムダを使用してnullでない場合にのみ値をフィルタリングする


160

私は言うオブジェクトのリストがありますcar。Java 8を使用して、いくつかのパラメーターに基づいてこのリストをフィルター処理したいのですが、パラメーターがの場合はnull、がスローされNullPointerExceptionます。null値を除外する方法は?

現在のコードは次のとおりです

requiredCars = cars.stream().filter(c -> c.getName().startsWith("M"));

これはスローNullPointerExceptionあればgetName()リターンnull


「nullでない場合にのみ値をフィルター」または「null値をフィルターで除外」したいですか?それは私には矛盾しているように思えます。
Holger

3
ツナキの答えは、実際にあなたの質問に答える唯一のものであると思われるので、受け入れてもらえますか。
マークブース

回答:


322

この特定の例では、@ Tagirは100%正しいと思います。1つのフィルターに入れ、2つのチェックを行います。私はOptional.ofNullableOptionalのものは実際には戻り値の型がロジックを実行しないようにするためのものですが、実際にはここにもそこにもありません。

java.util.Objects広いケースでこれには良い方法があることを指摘したかったので、これを行うことができます:

cars.stream()
    .filter(Objects::nonNull)

あなたのnullオブジェクトをクリアします。知らない人にとっては、これは以下の省略形です。

cars.stream()
    .filter(car -> Objects.nonNull(car))

手元の質問に部分的に回答して、次で始まる車名のリストを返します"M"

cars.stream()
    .filter(car -> Objects.nonNull(car))
    .map(car -> car.getName())
    .filter(carName -> Objects.nonNull(carName))
    .filter(carName -> carName.startsWith("M"))
    .collect(Collectors.toList());

簡単なラムダに慣れたら、次のようにすることもできます。

cars.stream()
    .filter(Objects::nonNull)
    .map(Car::getName)        // Assume the class name for car is Car
    .filter(Objects::nonNull)
    .filter(carName -> carName.startsWith("M"))
    .collect(Collectors.toList());

残念ながら、いったん.map(Car::getName)名前のリストだけが返され、車は返されません。それほど美しくはありませんが、完全に質問に答えます:

cars.stream()
    .filter(car -> Objects.nonNull(car))
    .filter(car -> Objects.nonNull(car.getName()))
    .filter(car -> car.getName().startsWith("M"))
    .collect(Collectors.toList());

1
ヌルカーは問題ではないことに注意してください。この場合、名前プロパティが問題を引き起こしています。だからObjects::nonNullここでは使用できません、そして最後のアドバイスではそれはcars.stream() .filter(car -> Objects.nonNull(car.getName()))私が信じているべきです
kiedysktos

1
ところで、私cars.stream() .filter(car -> Objects.nonNull(car.getName()) && car.getName().startsWith("M"))はこの質問のコンテキストであなたのアドバイスの要約になると思います
kiedysktos

3
@kiedysktosこれは、呼び出し.startWithがnullポインタを引き起こす可能性があるという点で良い点です。私が指摘しようとしていた点は、Javaがストリームからnullオブジェクトを除外するためのメソッドを提供しているということです。
xbakesx 2017年

@Mark Boothはい、明らかにObjects.nonNullと同等!= nullです。オプションは短くなります
kiedysktos

1
車(String)の代わりに車名()のリストを作成していませんCarか?
user1803551 2018年

59

null名前のある車をフィルタリングするだけです。

requiredCars = cars.stream()
                   .filter(c -> c.getName() != null)
                   .filter(c -> c.getName().startsWith("M"));

3
この回答が実際に質問に回答する唯一の回答であると思われるため、この回答の投票率が高くないのは本当に残念です。
マークブース

@MarkBooth「null値を除外する方法」という質問 xbakesxでよく答えられるようです。
vegemite4me 2017年

@MarkBoothあなたが正しい日付を見てください。私の間違い。
vegemite4me 2017年

パフォーマンスに関しては、ストリームを2回フィルター処理するのが適切ですか、それとも、述語をフィルター処理に使用する方が良いですか。ただ知りたいです。
Vaibhav_Sharma

51

提案された答えは素晴らしいです。ただ、使用してヌルリストのケースを処理するために改善を提案したいと思いOptional.ofNullableJavaの8の新機能を

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

したがって、完全な答えは次のようになります。

 List<String> carsFiltered = Optional.ofNullable(cars)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull) //filtering car object that are null
                .map(Car::getName) //now it's a stream of Strings
                .filter(Objects::nonNull) //filtering null in Strings
                .filter(name -> name.startsWith("M"))
                .collect(Collectors.toList()); //back to List of Strings

5
オプションの不適切な使用。そもそもnullを空のコレクションの同義語として使用しないでください。
VGR

4
@VGRもちろんですが、実際にはそうではありません。時々(ほとんどの場合)、多くの人が取り組んだコードを扱う必要があります。ときどき、外部インターフェースからデータを受け取ります。これらすべてのケースで、オプションは優れた用途です。
ジョニー

1
ヌルカーは問題ではないことに注意してください。この場合、名前プロパティが問題を引き起こしています。だから、Objects::nonNullnull以外の車が名前== nullを持つことができるので、問題を解決していません
kiedysktos

もちろん@kiedysktosですが、それは私が答えに示したかったものではありません。しかし、私はあなたが言ったことを受け入れ、その答えを編集しています:)
ジョニー

24

これは、単一のフィルターステップで実行できます。

requiredCars = cars.stream().filter(c -> c.getName() != null && c.getName().startsWith("M"));

getName()何度も呼び出したくない場合(たとえば、高額な呼び出し)、これを行うことができます。

requiredCars = cars.stream().filter(c -> {
    String name = c.getName();
    return name != null && name.startsWith("M");
});

またはより洗練された方法で:

requiredCars = cars.stream().filter(c -> 
    Optional.ofNullable(c.getName()).filter(name -> name.startsWith("M")).isPresent());

2番目の例のインライン展開は、私のユースケースにとって価値がありました
Paul

3

の力を活用java.util.Optional#map()

List<Car> requiredCars = cars.stream()
  .filter (car -> 
    Optional.ofNullable(car)
      .map(Car::getName)
      .map(name -> name.startsWith("M"))
      .orElse(false) // what to do if either car or getName() yields null? false will filter out the element
    )
  .collect(Collectors.toList())
;

1

あなたはこれを使うことができます

List<Car> requiredCars = cars.stream()
    .filter (t->  t!= null && StringUtils.startsWith(t.getName(),"M"))
    .collect(Collectors.toList());
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.