3

I have nested list and am able to set isMatched and department.setMatchedStatus(true) when if condition is 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;

Would like to achieve the same using java 8 streams, which i tried using below code, but couldn't return boolean.

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

Could anyone help me on this please ?

2
  • 2
    your original codes don't return bool either. you just set the attribute, the stream() codes do the same. Commented Dec 27, 2016 at 13:50
  • Sorry, Actually i missed it, i will just edit . Commented Dec 27, 2016 at 13:52

3 Answers 3

5

The difficulty here is that you have two side effects you want performed: setting the matched state on the Department object, and setting a local flag value to determine if there were any matches. The approach of using peek and count in sisyphus' answer will work, since in this case we can be assured that count won't short-circuit. However, it may cause problems under maintenance. If somebody were to copy and rearrange this code, it might silently break because of short-circuiting, and this would be quite subtle.

Perhaps a better approach is to pack the side effects into the forEach operation. This uses AtomicBoolean as a mutable "box" to work around the inability to mutate captured local variables. It's also preferable to the single-element array trick, as the atomics are safe in case the stream is run in parallel.

This also uses a statement lambda, which I generally prefer to avoid. In this case it's not too bad, and it makes clear that multiple side effects are occurring.

    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();
Sign up to request clarification or add additional context in comments.

Comments

3

You could use the 'peek()' method on the Stream, which allows you to consume the items in the stream without altering the contents of the stream. After you've updated each object you just need to know if any were matched.

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

6 Comments

Edited - the count() should consume the stream.
It might, but there is no guarantee that it will. I vaguely remember discussions about optimizations for array-based streams etc, will try to find something about that.
Javadoc says count() is equivalent to return mapToLong(e -> 1L).sum(); and is a reducing op. Seems like it should consume the whole stream.
@Hulk, in this particular case it still will be valid, as due to presense of flatMap and filter the stream size is not known in advance. However such code style (i.e. using peek for non-debugging purposes) is definitely bad.
Anything mutating objects within a Stream is going to be problematic at some point. Streams are not really designed or optimised for mutating stateful objects. If the object model was immutable then the Streams usage would probably be more clean & idiomatic.
|
0

To me, the most clear solution would be the following:

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

It's somewhat less efficient as produces an intermediate Set, but looks better than other proposed variants from the code readability point of view.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.