2

I wanted to know if the stream API of java handle both condition the same way or different? If its going to execute an independent loop for each filter, then in terms of performance there is a significate difference I think. How do you think it is?

Here are the two conditions

 1. filter(cond1 && cond2 && cond3)
 2. filter(cond1).filter(cond2).filter(cond3)

Example

List<Employee> emps1 = employees.stream()                                                     
        .filter((Employee e) -> e.name.endsWith("n") && e.salary > 10000 && e.id % 2 == 1)    
        .collect(Collectors.toList());                                                        
                                                                                              
List<Employee> emps2 = employees.stream()                                                     
        .filter(e -> e.name.endsWith("n"))                                                    
        .filter(e -> e.salary > 10000)                                                        
        .filter(e -> e.id % 2 == 1)                                                           
        .collect(Collectors.toList());                                                        
4
  • Reading the options, the first one will apply the condition concatenated with "ands" to the whole data, the others will apply cond1 to the whole, and the others to a reduced set each. My suggestion for you is to measure yourself with the data you have - I guess they will present differences even if you swap conditions order. Commented Feb 19, 2021 at 18:56
  • 3
    TLDR: Compiler handles it differently. We can refer to this stackoverflow.com/a/48513110/1285923. They did the analysis at the bytecode level of how the compiler handles it. For performance, IMO, it will not be significantly difference, the only optimization is in the operator short circuit. Commented Feb 19, 2021 at 19:03
  • 1
    code it on what is easier to read, and the second one seems like the way to do it. performance wise, there will be close to no diff Commented Feb 19, 2021 at 19:25
  • 1
    Any performance question can only truly be answered with benchmarking. Hypotheticals only go so far. I suggest you benchmark the code and find out. Commented Feb 19, 2021 at 20:32

1 Answer 1

2

In the case of filter(cond1 && cond2 && cond3), the processing of condition stops as soon as any of the conditions evaluates to false e.g. if cond1 evaluates to false, the other conditions (cond2 and cond3) won't be processed. Similarly, if cond1 evaluates to true, the processing will proceed to the evaluation of cond2 and if this evaluates to false, the condition, cond3 will not be evaluated.

The processing of filter(cond1).filter(cond2).filter(cond3) too happens in the same manner, as can be seen from the following example:

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream.of("one", "two", "three", "four")
        .filter(s -> {
            System.out.println("Hello");
            return s.contains("t");
        })
        .filter(s -> {
            System.out.println("Hi");
            return s.contains("f");
        })
        .forEach(System.out::println);
    }
}

Output:

Hello
Hello
Hi
Hello
Hi
Hello

Thus, it does not make any difference and it's a matter of choice. The second one looks cleaner.


Note: For more complex expressions, you can use Predicate#and which gives you two more benefits:

  1. You can reuse the behaviour implemented by the Predicate with other streams in your code.
  2. Further clean code.

Demo:

import java.util.function.Predicate;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Predicate<String> containsT = s -> s.contains("t");
        Predicate<String> containsE = s -> s.contains("e");
        
        Stream.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten")
                .filter(containsT.and(containsE))
                .forEach(System.out::println);
    }
}

Output:

three
eight
ten

However, it doesn't make any difference in the way the processing happens.

Sign up to request clarification or add additional context in comments.

2 Comments

"In the case of filter(cond1).filter(cond2).filter(cond3), all the conditions will always be evaluated." - got a source for that? I don't believe that's universally true, if at all.
I know how stream processing works, but the idea that all filters get executed for every element is just wrong; if they don't pass the first filter, consecutive ones don't get evaluated. ideone.com/yjOS8u

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.