Stream API anyMatch() 具有多个可为空的嵌套对象

Stream API allMatch() with mulitple nullable nested objects

我正在处理请求验证功能,我需要检查某个字符串值是否存在于包含在对象中的 属性 中,该对象包含为映射条目的值,该地图是对象的一部分,最后我的请求正文包含一组这些对象。

为了让它更清晰,我将仅使用重要的代码部分来重现我的情况。

假设我的 ClassA 在请求正文中收到,它具有以下属性:

@Nullable
Set<ClassB> mySet = null;

现在,我们 ClassB 有一个 ClassC 类型的特定包装对象:

@Nullable 
ClassC classC;

包装器 class ClassC 包含一个映射:

@Nullable
Map<String, ClassD> classDMap = new HashMap<>;

最后,ClassD包含需要检查的属性是否包含某个值:

@Nullable
String propertyForValidation;

目前我使用的代码如下所示:

public boolean isValid(ClassA body) {
  return body.getMySet().stream()
                        .allMatch(x -> x.getClassC().getClassDMap().values().stream()
                        .allMatch(y -> y.getPropertyForValidation.equals("someValue"));
}

我的代码当前存在的问题是,当我上面描述的某些对象为空时,它无法按预期工作,因为我得到了很多 NullPointerException。我想这可以通过大量 ifs 和 null 检查来解决,但我想知道是否有更优雅的方法使用 Stream API?

感谢所有帮助,谢谢。

我会尝试使用 Stream.filter()Stream.filter(x -> x!=null) 之类的东西应该可以代替(并且比 if 语句更优雅。

在您的情况下,恐怕在您的每个对象或字段(可为空)getters 之前都有必要。但这是我最喜欢的方式。

对于空地图的情况,您也可以将它与 isEmpty() 方法结合使用。

另一种方法是在 getter 函数中 添加 if 语句。这将允许您保留您已经编写的确切流。

这是一个使用单个 Stream 的解决方案。 它有点重复,但应该符合您的预期结果。

注意用法:

  • Objects#nonNull 使用方法引用检查对象的无效性
  • 文字String首先与equals一起使用的方式避免了最后NullPointerException可能的

public boolean isValid(ClassA body) {
    return ofNullable(body.getMySet())
               .orElse(emptySet())
               .stream()
               .filter(Objects::nonNull)
               .map(ClassB::getClassC)
               .filter(Objects::nonNull)
               .map(ClassC::getClassDMap)
               .filter(Objects::nonNull)
               .flatMap(map -> map.values().stream())
               .allMatch(y -> "someValue".equals(y.getPropertyForValidation()));
}

受到@Turing85 评论的启发,我尝试用 Optionals 解决这个问题。我 推荐这种方法,但认为在这里分享可能具有教育意义!

当相应的组件 missing/set 为空时,每个 orElse(Boolean.TRUE) 的效果是 return true(如果缺少的组件应该无法通过验证,请将其更改为 Boolean.FALSE).如果我们可以假设在 ClassDMap 上没有设置 null 值,那么 Stream::allMatch 可以直接在 ClassD 上工作而不是 Optional<ClassD>.幸运的是,最后的 String::equals 比较提供了一个 null 免费检查 return 由 ClassD::getPropertyForValidation 编辑的值。

public boolean isValid(ClassA body) {
   return Optional.ofNullable(body)
           .flatMap(a -> Optional.ofNullable(a.getMySet()))
           .map(Set::stream)
           .map(streamB -> streamB
                   .map(b -> Optional.ofNullable(b.getClassC()))
                   .map(opC -> opC.flatMap(c -> Optional.ofNullable(c.getClassDMap())))
                   .map(opMapD -> opMapD
                           .map(map -> map.values().stream()
                                   .map(d -> Optional.ofNullable(d))
                                   .allMatch(opD -> opD
                                           .map(d -> "someValue".equals(d.getPropertyForValidation()))
                                           .orElse(Boolean.TRUE)))
                           .orElse(Boolean.TRUE))
                   .reduce((acc, item) -> acc && item)
                   .orElse(Boolean.TRUE))
           .orElse(Boolean.TRUE);
}