基于条件设置对象值并使用java 8流返回布尔值
我有嵌套列表,如果条件为真,我可以设置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));
有人可以帮我吗?
这里的难点在于您有两个需要执行的副作用:在Department
对象上设置匹配状态,并设置本地标志值以确定是否存在任何匹配。 在sisyphus的回答中使用peek
和count
的方法是有效的,因为在这种情况下我们可以确保count
不会短路。 但是,它可能会导致维护问题。 如果有人复制并重新安排这些代码,它可能会因为短路而无声地中断,这将是非常微妙的。
也许更好的方法是将副作用打包到forEach
操作中。 这使用AtomicBoolean
作为可变“框”来解决无法改变捕获的局部变量的问题。 它也优于单元素数组技巧,因为在并行运行流时,primefaces是安全的。
这也使用了一个语句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();
您可以在Stream上使用’peek()’方法,它允许您使用流中的项而不更改流的内容。 更新每个对象后,您只需要知道是否匹配。
return company.getEmployees().stream() .flatMap(employee-> employee.getDepartments().stream()) .filter((department) -> departmentList.contains(department.getDepartmentName())) .peek((department) -> department.setMatchedStatus(true)) .count() > 0;
对我来说,最明确的解决方案如下:
Set 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
效率稍低,但从代码可读性的角度来看,它看起来比其他提出的变体更好。