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());                                                        
Kidus Tekeste
  • 651
  • 2
  • 10
  • 28
  • 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. – Leonardo Alves Machado Feb 19 '21 at 18:56
  • 3
    TLDR: Compiler handles it differently. We can refer to this https://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. – Wilianto Indrawan Feb 19 '21 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 – Eugene Feb 19 '21 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. – Abhijit Sarkar Feb 19 '21 at 20:32

1 Answers1

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.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • 1
    "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. – daniu Feb 19 '21 at 20:17
  • 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. https://ideone.com/yjOS8u – daniu Feb 20 '21 at 09:42