【问题标题】:Filter out null values in Java 8 stream filter expression so exception is not thrown过滤掉 Java 8 流过滤器表达式中的空值,因此不会引发异常
【发布时间】:2020-06-25 21:53:57
【问题描述】:

我有一个 Java 8 流表达式,它有 3 个过滤器并且工作正常。 对于大多数值,我想防止过滤器中出现空指针异常。 这是表达式:

if(!purchasedTripSegments.isEmpty()) {
    filteredList = purchasedTripSegments.stream()
        .filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
        .filter(distinctByKeys(segment -> Arrays.asList(segment.getBillingMethod(), 
            segment.getOrigin().getNumberCode(), segment.getDestination().getNumberCode(), 
            segment.getStopOff().getStopOffLocation().getNumberCode()))) 
        .filter(segment -> segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL) ||
               (segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_RULE) &&
                segment.getDestination().getNumberCode() != 
                  segment.getStopOff().getStopOffLocation().getNumberCode())) 
        .collect(Collectors.toList());
}

所以 VendorType 不能为空。 所以第一个过滤器会很好。 第二个和第三个过滤器可以有空值。 对象(Origin、Destination、StopOff、StopOffLocation)可以为空。 并且值 (BillingMethod, NumberCode) 可以为空。

如果过滤器中的任何值为空值,有没有办法忽略过滤器?

我尝试添加.filter(Objects::nonNull) 我有一个测试用例,它的目标对象为空,并且抛出了 NullPointerException。

更新 我更新了计费方法。但我不清楚如何使用 Optional 来避免空检查。

Optional<List<PurchasedTripSegment>> filteredList =  Optional.ofNullable(new ArrayList<>());

if(!purchasedTripSegments.isEmpty()) {
    filteredList = purchasedTripSegments.stream()
     .filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
     .filter(distinctByKeys(segment -> Arrays.asList(segment.getBillingMethod(), 
                            segment.getOrigin().getNumberCode(), 
                            segment.getDestination().getNumberCode(), 
                    segment.getStopOff().getStopOffLocation().getNumberCode()))) 
     .filter(segment -> BILLING_METHOD_LOCAL.equals(segment.getBillingMethod()) 
                  || (BILLING_METHOD_RULE.equals(segment.getBillingMethod()) &&
                               segment.getDestination().getNumberCode() != 
                      segment.getStopOff().getStopOffLocation().getNumberCode())) 
      .collect(Collectors.toList());
}

我不确定如何将您建议的更改应用到我的过滤器。我尝试按书面添加,但 map() 未被识别。 中间过滤器将是最困难的。 如何检查每个段的对象和值?

更新 根据下面的评论,使用 Optional 实现实用程序方法。

private Optional<Integer> getDestinationCode(PurchasedCostTripSegment purchasedCostTripSegment) {
        return Optional.ofNullable(purchasedCostTripSegment.getDestination()) // empty for 'null'
                .map(Destination::getNumberCode);
    }

我对传入参数进行空检查。 我收到一个错误,指出方法 getNumberCode 无法识别。

【问题讨论】:

    标签: java java-stream


    【解决方案1】:

    诸如billingMethod 之类的属性,只要它可能是nullList 内,它应该仍然可以用于比较以获得不同的值。 另一方面,将它们与其他一些字符串常量进行比较可以通过用户FilipRistic suggested 的方式来解决。

    但是,当涉及可能为 null 的对象并且您希望更安全地访问内部属性时,您可以使用 Optional 并链接访问器。对于其中的一个示例,当您想要访问可能为空的目的地的 numberCode 时,您可以在 PurchasedTripSegment 类中使用访问器来公开它:

    Optional<Integer> getDestinationCode() {
        return Optional.ofNullable(this.getDestination()) // empty for 'null'
                       .map(Node::getNumberCode);
    }
    

    通过对其他访问器进行类似更改,您的整体代码将更新并更改为:

    filteredList = purchasedTripSegments.stream()
            .filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
            .filter(distinctByKey(segment -> Arrays.asList(segment.getBillingMethod(),
                    segment.getOriginCode(), segment.getDestinationCode(),
                    segment.getStopOffLocationCode())))
            .filter(segment -> segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL) ||
                    (segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_RULE) &&
                            segment.getDestinationCode().equals(segment.getStopOffLocationCode())))
            .collect(Collectors.toList());
    

    【讨论】:

    • 感谢您的解释!让我来解决这个问题。
    • 但是有一个问题...为什么getNumberCode 是 Node 类型的?
    • @GloriaSantin 我只是模仿了Destination 的类并将其命名为Node,如果这是您的类名,您可以将其替换为DestinationnumberCode 的类型仍然是 Integer,这只是一个映射操作。
    • 我无权访问作为 Destination 的 PurchaseTripSegment 类或 Location 类。因此,我在需要此功能的实用程序类中创建了一个方法。我将 PurchaseTripSegment 作为参数传入。要将 Optional 功能添加到 Location 类中以使 getNumberCode 方法起作用,我是否会在 getDestinationCode 方法中添加另一个 Optional.ofNullable ?
    • @GloriaSantin 如果它是您要介绍的实用方法,那么它可能类似于Optional&lt;Integer&gt; getDestinationCode(PurchaseTripSegments segment) { return Optional.ofNullable(segment.getDestination()) // empty for 'null' .map(Node::getNumberCode); }。当然,这取决于segmentNonnull,否则Optional.ofNullable(segment).map().map() 可以替代。
    【解决方案2】:

    不,过滤器没有任何方法可以知道,因为它不知道您将以何种方式使用 Predicate 中的元素,所以您唯一的解决方案是自己执行 null 检查。

    请注意,您可以避免在与您知道不为空的常量进行比较的情况下进行检查,而不是编写:

    segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL)
    

    你可以这样写:

    BILLING_METHOD_LOCAL.equalsIgnoreCase(segment.getBillingMethod())
    

    这将避免 NPE,但它仅在少数情况下对您有所帮助,而不是全部,对于其他情况,您将不得不执行检查或重构以返回类型 Optional,您的情况可能如下所示:

    segment.getDestination()
        .flatMap(d -> segment.getStopOff()
            .map(s -> s.getStopOffLocation)
            .filter(s -> s.getNumberCode() == d.getNumberCode()) )
        .isPresent();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-31
      相关资源
      最近更新 更多