Filter values only if not null using lambda in Java8

327,858

Solution 1

In this particular example, I think @Tagir is 100% correct get it into one filter and do the two checks. I wouldn't use Optional.ofNullable the Optional stuff is really for return types not to be doing logic... but really neither here nor there.

I wanted to point out that java.util.Objects has a nice method for this in a broad case, so you can do this:

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

Which will clear out your null objects. For anyone not familiar, that's the short-hand for the following:

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

To partially answer the question at hand to return the list of car names that starts with "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());

Once you get used to the shorthand lambdas you could also do this:

    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());

Unfortunately once you .map(Car::getName) you'll only be returning the list of names, not the cars. So less beautiful but fully answers the question:

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

Solution 2

You just need to filter the cars that have a null name:

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

Solution 3

The proposed answers are great. Just would like to suggest an improvement to handle the case of null list using Optional.ofNullable, new feature in Java 8:

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

So, the full answer will be:

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

Solution 4

You can do this in single filter step:

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

If you don't want to call getName() several times (for example, it's expensive call), you can do this:

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

Or in more sophisticated way:

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

Solution 5

Leveraging the power of 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())
;
Share:
327,858

Related videos on Youtube

vaibhavvc1092
Author by

vaibhavvc1092

Updated on December 15, 2021

Comments

  • vaibhavvc1092
    vaibhavvc1092 over 2 years

    I have a list of objects say car. I want to filter this list based on some parameter using Java 8. But if the parameter is null, it throws NullPointerException. How to filter out null values?

    Current code is as follows

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

    This throws NullPointerException if getName() returns null.

    • Holger
      Holger over 8 years
      Do you wanna “filter values only if not null” or “filter out null values”? That sounds contradicting to me.
    • MarkSwears
      MarkSwears almost 7 years
      Could I suggest that you accept Tunaki's answer as it appears to be the only one which actually answers your question.
    • JustAnotherCoder
      JustAnotherCoder over 2 years
      What about kotlin? )) requiredCars = cars.filter {c -> c?.name?.startsWith("M"))};
  • VGR
    VGR over 7 years
    Bad use of Optional. null should never be used as a synonym for an empty Collection in the first place.
  • Johnny
    Johnny over 7 years
    @VGR Of course, but that is not what happens in practice. Sometimes (most of the time) you need to work with code that a lot of people worked on. Sometime you receive your data from external interfaces. For all those cases, Optional is a great use.
  • kiedysktos
    kiedysktos over 7 years
    note that the null car isn't the issue. In this case, it's name property causing problems. So Objects::nonNull can't be used here, and in last advice it should be cars.stream() .filter(car -> Objects.nonNull(car.getName())) I believe
  • kiedysktos
    kiedysktos over 7 years
    BTW, I think cars.stream() .filter(car -> Objects.nonNull(car.getName()) && car.getName().startsWith("M")) would be the summary of your advice in this question context
  • kiedysktos
    kiedysktos over 7 years
    note that the null car isn't the issue. In this case, it's name property causing problems. So Objects::nonNull doesn't solve the problem since non-null car can have name==null
  • xbakesx
    xbakesx over 7 years
    @kiedysktos That's a good point that calling .startWith could also cause a null pointer. The point I was trying to make is that Java supplies a method specifically for filtering out null objects from your streams.
  • MarkSwears
    MarkSwears almost 7 years
    It's a real shame that this answer isn't more highly voted as it appears to be the only answer that actually answers the question.
  • kiedysktos
    kiedysktos almost 7 years
    @Mark Booth yes, obviously Objects.nonNull is equivalent to != null, your option is shorter
  • vegemite4me
    vegemite4me almost 7 years
    @MarkBooth The question "How to filter out null values?" seems to be answered well by xbakesx.
  • vegemite4me
    vegemite4me almost 7 years
    @MarkBooth Looking at the dates you are correct. My mistake.
  • Johnny
    Johnny over 6 years
    Of course @kiedysktos, but that's not what I wanted to show in the answer. But, I'm accepting what you saying and editing the answer :)
  • user1803551
    user1803551 over 5 years
    Aren't you creating a list of car names (String) instead cars (Car)?
  • xbakesx
    xbakesx over 5 years
    Definitely right, once you map to getName it's just a list of Strings. I'll update the answer. Thank you!
  • Vaibhav_Sharma
    Vaibhav_Sharma about 5 years
    Performance wise is it good to filter the stream twice Or better use the predicate for filtering? Just want to know.
  • Aníbal
    Aníbal about 5 years
    It's more efficient to use the method reference Objects:nonNull (first option) than using the arrow operator that instance an object.
  • Paul
    Paul about 4 years
    The inline expansion in the second example was valuable for my use case