Chaining Multiple Predicates in Java

Learn to combine multiple Predicate instances aka chained predicates and perform ‘logical AND‘ and ‘logical OR‘ operations on the Stream filter() operation. Chained predicates are useful in filtering the stream items for multiple conditions.

1. How to Use Predicates

Predicates are used for filtering the items from a stream. For example, if a have a stream of strings and want to find all the strings starting with ‘A‘, we can create a Predicate using the lambda expression.

Predicate<String> startsWithA = s -> s.startsWith("A");

Now use this predicate with Stream.filter() method.

List<String> list = Arrays.asList("Aa", "Bb", "Cc", "Dd", "Ab", "Bc");

Predicate<String> startsWithA = s -> s.startsWith("A");

List<String> items = list.stream()
  .filter(startsWithA)
  .collect(Collectors.toList());

System.out.println(items);

2. Predicate Chain

The first example is of a simple predicate or single condition. In real-world applications, we may be filtering the items on multiple conditions.

2.1. Simple Example

A good way to apply such complex conditions is by combining multiple simple conditions to make one complex condition.

For example, if we want to get all strings that start with either A or B but it must not contain the letter ‘c‘. Lets create the Predicate for this:

Predicate<String> startsWithA = s -> s.startsWith("A");
Predicate<String> startsWithB = s -> s.startsWith("B");
Predicate<String> notContainsC = s -> !s.contains("c");

Predicate<String> complexPredicate = startsWithA.or(startsWithB)
  .and(notContainsC);

Note that for creating the negative conditions, we can use the method negate() on the position predicates.

Predicate<String> containsC = s -> s.contains("c");
Predicate<String> negatedPredicate = containsC.negate();

Predicate<String> notContainsC = s -> !s.contains("c");

In the above example, negatedPredicate and notContainsC will have the same effect on the filter() operation.

2.2. The and() Method for Logical AND Operation

  • The and() method returns a composed predicate that represents a short-circuiting logical AND of given predicate and another.
  • When evaluating the composed predicate, if the first predicate is false, then the other predicate is not evaluated.
  • Any exceptions thrown during evaluation of either predicate are relayed to the caller; if evaluation of first predicate throws an exception, the other predicate will not be evaluated.

In the given example, we are finding all the employees whose id is less than 4 and salary is greater than 200.

List<Employee> employeesList = Arrays.asList(
          new Employee(1, "Alex", 100),
          new Employee(2, "Brian", 200),
          new Employee(3, "Charles", 300),
          new Employee(4, "David", 400),
          new Employee(5, "Edward", 500),
          new Employee(6, "Frank", 600)
        );

Predicate<Employee> idLessThan4 = e -> e.getId() < 4;

Predicate<Employee> salaryGreaterThan200 = e -> e.getSalary() > 200;

List<Employee> filteredEmployees = employeesList.stream()
    .filter( idLessThan4.and( salaryGreaterThan200 ) )
    .collect(Collectors.toList());

System.out.println(filteredEmployees);

Program Output.

[Employee [id=3, name=Charles, salary=300.0]]

2.3. The or() Method for Logical OR Operation

  • The Predicate.or() method returns a composed predicate that represents a short-circuiting logical OR of given predicate and another predicate.
  • When evaluating the composed predicate, if the first predicate is true, then the other predicate is not evaluated.
  • Any exceptions thrown during evaluation of either predicate are relayed to the caller; if evaluation of first predicate throws an exception, the other predicate will not be evaluated.

In the given example, we are finding all the employees whose id is less than 2 or salary is greater than 500.

List<Employee> employeesList = Arrays.asList(
              new Employee(1, "Alex", 100),
              new Employee(2, "Brian", 200),
              new Employee(3, "Charles", 300),
              new Employee(4, "David", 400),
              new Employee(5, "Edward", 500),
              new Employee(6, "Frank", 600)
            );

Predicate<Employee> idLessThan2 = e -> e.getId() < 2;

Predicate<Employee> salaryGreaterThan500 = e -> e.getSalary() > 500;

List<Employee> filteredEmployees = employeesList.stream()
        .filter( idLessThan2.or( salaryGreaterThan500 ) )
        .collect(Collectors.toList());

System.out.println(filteredEmployees);

Program output.

[Employee [id=1, name=Alex, salary=100.0], 
Employee [id=6, name=Frank, salary=600.0]]

3. Conclusion

In this Java tutorial, we learned to create simple predicates and use them to filter the Stream items. Then we learned to combine multiple simple predicates to create complex predicates using and(), or() and negate() methods.

Happy Learning !!

Sourcecode on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode