在 Java8 中使用 lambda 仅在不为空时过滤值

Posted

技术标签:

【中文标题】在 Java8 中使用 lambda 仅在不为空时过滤值【英文标题】:Filter values only if not null using lambda in Java8 【发布时间】:2015-12-29 07:23:22 【问题描述】:

我有一个对象列表,比如car。我想使用 Java 8 根据一些参数过滤这个列表。但是如果参数是null,它会抛出NullPointerException。如何过滤掉空值?

当前代码如下

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

如果getName() 返回null,则抛出NullPointerException

【问题讨论】:

您想要“仅过滤非空值”还是“过滤掉空值”?这对我来说听起来很矛盾。 我可以建议您接受Tunaki's answer,因为它似乎是唯一真正回答您问题的人。 科特林呢? )) requiredCars = cars.filter c -> c?.name?.startsWith("M")); 【参考方案1】:

在这个特定的例子中,我认为@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());

【讨论】:

请注意,空车不是问题。在这种情况下,它的 name 属性会导致问题。所以Objects::nonNull不能在这里使用,在最后的建议中应该是cars.stream() .filter(car -> Objects.nonNull(car.getName()))我相信 顺便说一句,我认为cars.stream() .filter(car -> Objects.nonNull(car.getName()) && car.getName().startsWith("M")) 将是您在此问题上下文中的建议摘要 @kiedysktos 很好,调用.startWith 也可能导致空指针。我试图说明的一点是,Java 提供了一种专门用于从流中过滤掉空对象的方法。 @Mark Booth 是的,显然Objects.nonNull 等价于!= null,您的选择更短 您不是在创建汽车名称列表 (String) 而不是汽车 (Car) 吗?【参考方案2】:

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

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

【讨论】:

很遗憾,这个答案没有得到更高的投票,因为它似乎是唯一真正回答问题的答案。 @MarkBooth 问题“如何过滤掉空值?” xbakesx 似乎回答得很好。 @MarkBooth 查看日期,您是正确的。我的错。 性能明智,过滤两次流更好还是更好地使用谓词进行过滤?只是想知道。【参考方案3】:

建议的答案很棒。只是想提出一个改进,以使用Optional.ofNullable、new feature in Java 8 处理null 列表的情况:

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

【讨论】:

Optional 使用不当。 null 一开始就不应该用作空集合的同义词。 @VGR 当然,但实际情况并非如此。有时(大部分时间)您需要使用很多人都在编写的代码。有时您会从外部接口接收数据。对于所有这些情况,Optional 都非常有用。 请注意,空车不是问题。在这种情况下,它的 name 属性会导致问题。所以Objects::nonNull 没有解决问题,因为非空汽车可以有 name==null 当然@kiedysktos,但这不是我想在答案中显示的。但是,我接受你所说的并编辑答案:)【参考方案4】:

您可以在单个过滤步骤中执行此操作:

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

【讨论】:

第二个例子中的内联扩展对我的用例很有价值【参考方案5】:

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

【讨论】:

【参考方案6】:

你可以用这个

List<Car> requiredCars = cars.stream()
    .filter (t->  t!= null && StringUtils.startsWith(t.getName(),"M"))
    .collect(Collectors.toList());

【讨论】:

以上是关于在 Java8 中使用 lambda 仅在不为空时过滤值的主要内容,如果未能解决你的问题,请参考以下文章

仅在不为空时比较字段

Bigquery 仅在不为空时选择列

仅在选中且不为空时才显示元素

Laravel 验证仅在字段不为空时验证正则表达式

VBScript 仅在文件不为空时附加文件

仅当查询不为空时,才从查询写入 BigQuery 中的表