仅在Java8中使用lambda过滤值,如果不为null


160

我有一个对象清单说car。我想使用Java 8根据某些参数过滤此列表。但是,如果参数为null,则抛出NullPointerException。如何过滤出空值?

当前代码如下

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

NullPointerException如果getName()返回则抛出该异常null


您是否想“仅在不为null时过滤值”或“过滤出null值”?这听起来与我矛盾。
Holger 2015年

3
我可以建议您接受Tunaki的答案,因为它似乎是唯一可以实际回答您问题的答案。
马克·布斯,

Answers:


322

在这个特定示例中,我认为@Tagir是100%正确的,将其放入一个过滤器中并进行两次检查。我不会Optional.ofNullable将Optional的东西真正用于返回类型而不是做逻辑...但是实际上无论在这里还是那里。

我想指出,java.util.Objects在广泛的情况下,这有一个不错的方法,因此您可以执行以下操作:

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

这将清除您的空对象。对于任何不熟悉的人,这是以下内容的简写:

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

一旦习惯了速记lambda,您还可以执行以下操作:

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也可能导致空指针。我要说明的是Java提供了一种专门用于从流中过滤出空对象的方法。
xbakesx

@Mark Booth是的,显然Objects.nonNull等价于!= null,您的选择更短
kiedysktos

1
您不是在创建汽车名称列表(String)而是创建汽车列表(Car)吗?
user1803551

59

您只需要过滤具有以下null名称的汽车:

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

3
真是遗憾的是,该答案未获得较高的投票,因为它似乎是实际回答该问题的唯一答案。
马克·布斯,

@MarkBooth问题“如何过滤出空值?” xbakesx似乎很好地回答了这个问题。
vegemite4me,2015年

@MarkBooth查看正确的日期。我的错。
vegemite4me

性能明智的做法是对流进行两次过滤,还是更好地使用谓词进行过滤?只是想知道。
Vaibhav_Sharma

51

建议的答案很棒。只是想提出一个改进使用处理空单的情况下Optional.ofNullable用Java 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
错误使用Optional。首先,不应将null用作空Collection的同义词。
VGR

4
@VGR当然,但这不是实际发生的情况。有时(大部分时间),您需要使用很多人都在处理的代码。有时您会从外部接口接收数据。对于所有这些情况,“可选”是一个很好的用途。
约翰尼

1
请注意,空车不是问题。在这种情况下,它的名称属性会引起问题。因此Objects::nonNull无法解决问题,因为非
空车

当然是@kiedysktos,但这不是我想在答案中显示的内容。但是,我接受您的发言并编辑答案:)
Johnny

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

第二个示例中的内联扩展对于我的用例非常有价值
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());
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.