【问题标题】:Based on condition set object value and return boolean using java 8 streams基于条件集对象值并使用 java 8 流返回布尔值
【发布时间】:2017-05-11 19:30:43
【问题描述】:

我有嵌套列表,如果条件为真,我可以设置 isMatched 和 department.setMatchedStatus(true)。

boolean isMatched = false;
for (Employee employee: company.getEmployees()) {
    for (Department department: employee.getDepartments()) {
        if(departmentList.contains(department.getDepartmentName())){
             isMatched = true;
             department.setMatchedStatus(true);
        }
    }
}
return isMatched;

希望使用 java 8 流实现相同的功能,我尝试使用以下代码,但无法返回布尔值。

 isMatched = company.getEmployees().stream()
.flatMap(employee-> employee.getDepartments().stream())
.filter((department) -> departmentList.contains(department.getDepartmentName()))
.forEach((department) -> department.setMatchedStatus(true));

有人可以帮我解决这个问题吗?

【问题讨论】:

  • 您的原始代码也不返回 bool。你只需设置属性,stream() 代码也一样。
  • 对不起,其实我错过了,我只是编辑。

标签: java java-8 java-stream


【解决方案1】:

这里的困难在于你有两个想要执行的副作用:在Department 对象上设置匹配状态,以及设置一个本地标志值以确定是否有任何匹配。在sisyphus' answer 中使用peekcount 的方法将起作用,因为在这种情况下,我们可以确保count 不会短路。但是,它可能会在维护时引起问题。如果有人要复制和重新排列这段代码,它可能会因为短路而悄无声息地中断,这将是非常微妙的。

也许更好的方法是将副作用打包到forEach 操作中。这使用AtomicBoolean 作为可变“盒子”来解决无法改变捕获的局部变量的问题。它也比单元素数组技巧更可取,因为在流并行运行的情况下原子是安全的。

这也使用了一个语句 lambda,我通常更愿意避免使用它。在这种情况下,它并不算太糟糕,并且清楚地表明正在发生多种副作用。

    AtomicBoolean isMatched = new AtomicBoolean(false);
    company.getEmployees().stream()
           .flatMap(employee -> employee.getDepartments().stream())
           .filter(department -> departmentList.contains(department.getDepartmentName()))
           .forEach(department -> {
               department.setMatchedStatus(true);
               isMatched.set(true);
           });
    return isMatched.get();

【讨论】:

    【解决方案2】:

    您可以在 Stream 上使用 'peek()' 方法,它允许您在不更改流内容的情况下使用流中的项目。更新每个对象后,您只需要知道是否有任何匹配。

    return company.getEmployees().stream()
                   .flatMap(employee-> employee.getDepartments().stream())
                   .filter((department) -> departmentList.contains(department.getDepartmentName()))
                   .peek((department) -> department.setMatchedStatus(true))
                   .count() > 0;
    

    【讨论】:

    • 已编辑 - count() 应该使用流。
    • 可能,但不能保证一定会。我隐约记得有关基于数组的流等优化的讨论,将尝试找到有关此的内容。
    • Javadoc 说 count() 等同于 return mapToLong(e -> 1L).sum(); 并且是减少操作。似乎它应该消耗整个流。
    • @Hulk,在这种特殊情况下,它仍然有效,因为存在flatMapfilter,流的大小事先不知道。然而这样的代码风格(即使用peek 用于非调试目的)绝对是不好的。
    • Stream 中的任何变异对象在某些时候都会出现问题。流并不是真正为改变有状态对象而设计或优化的。如果对象模型是不可变的,那么 Streams 的使用可能会更加简洁和惯用。
    【解决方案3】:

    对我来说,最明确的解决方案如下:

    Set<Department> matchingDepartments =
        company.getEmployees().stream()
               .flatMap(employee -> employee.getDepartments().stream())
               .filter(department -> departmentList.contains(department.getDepartmentName()))
               .collect(Collectors.toSet());
    matchingDepartments.forEach(department -> department.setMatchedStatus(true));
    return !matchingDepartments.isEmpty();
    

    生成中间 Set 的效率稍低,但从代码可读性的角度来看,它看起来比其他提议的变体更好。

    【讨论】:

      猜你喜欢
      • 2017-05-24
      • 2022-12-03
      • 1970-01-01
      • 1970-01-01
      • 2018-05-24
      • 1970-01-01
      • 2018-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多