Java Predicates

In mathematics, a predicate is commonly understood to be a boolean-valued function 'P:X ? {true, false}', called the predicate on X. Let’s learn how Java Predicate interface helps in writing filter expressions so easy.

1. Java Predicate Interface

1.1. When to use Predicates

Introduced in Java 8, Predicate is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

So, where do you think, we can use these true/false returning functions in day-to-day programming? I will say we can use predicates anywhere we need to evaluate a condition on a collection of objects such that evaluation can result either in true or false.

For example, we can use predicates in these real-life usecases:

  • Find all children born after a particular date
  • Pizzas ordered within a specific time range
  • Employees older than certain age
  • and so on…

1.2. Using Predicate with Stream

As we know, Predicate is a functional interface that means we can pass it in lambda expressions wherever a predicate is expected. For example, one such method is filter() method from Stream interface.

/**
 * Returns a stream consisting of the elements of this stream that match
 * the given predicate.
 *
 * <p>This is an <a href="package-summary.html#StreamOps">intermediate
 * operation</a>.
 *
 * @param predicate a non-interfering stateless predicate to apply to each element to determine if it
 * should be included in the new returned stream.
 * @return the new stream
 */
Stream<T> filter(Predicate<? super T> predicate);

We can assume a Stream as a mechanism to create a sequence of elements supporting sequential and parallel aggregate operations. It means we can anytime collect and perform some operation of all elements present in the stream in one call.

So, essentially we can use stream and predicate to –

  • first filter certain elements from a group, and
  • then perform some operation on filtered elements.

2. Predicate Examples

To demonstrate the uses of Predicate with Java Stream, we have an Employee class as below:

public class Employee {

   private Integer id;
   private Integer age;
   private String gender;
   private String firstName;
   private String lastName;
   
   //Getters and setters are hidden for brevity
}

2.1. Creating Predicates

As mentioned earlier, predicates evaluate an expression and return a boolean value. Now let us see a few examples.

  • Predicate to find all employees who are male and age more than 21
public static Predicate<Employee> isAdultMale()
{
    return p -> p.getAge() > 21 && p.getGender().equalsIgnoreCase("M");
}
  • Predicate to find all employees who are female and age more than 18
public static Predicate<Employee> isAdultFemale()
{
    return p -> p.getAge() > 18 && p.getGender().equalsIgnoreCase("F");
}
  • Predicate to find all employees whose age is more than a given age
public static Predicate<Employee> isAgeMoreThan(Integer age)
{
    return p -> p.getAge() > age;
}

We can build more of them as and when needed.

2.2. Filtering Streams with Predicate

Let’s use the predicates created above. When we pass the predicate in the Stream.filter() method, it returns a new stream with matching items only, that we can collect in a new List.

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
 
public class EmployeePredicates
{
    public static Predicate<Employee> isAdultMale() {
        return p -> p.getAge() > 21 && p.getGender().equalsIgnoreCase("M");
    }
 
    public static Predicate<Employee> isAdultFemale() {
        return p -> p.getAge() > 18 && p.getGender().equalsIgnoreCase("F");
    }
 
    public static Predicate<Employee> isAgeMoreThan(Integer age) {
        return p -> p.getAge() > age;
    }
 
    public static List<Employee> filterEmployees (List<Employee> employees,
                                                Predicate<Employee> predicate)
    {
        return employees.stream()
                    .filter( predicate )
                    .collect(Collectors.<Employee>toList());
    }
}

In the above example, we have created another utility method filterEmployees() that basically makes code clean and less repetitive. We can also combine more than one predicate to make a predicate chain or complex predicate, as we do in builder pattern.

So, in this function, we pass the list of employees and we pass a predicate, then this function will return a new collection of employees satisfying the condition mentioned in parameter predicate.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static predicateExample.EmployeePredicates.*;
 
public class TestEmployeePredicates
{
    public static void main(String[] args)
    {
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");
 
        List<Employee> employees = new ArrayList<Employee>();
        employees.addAll(Arrays.asList(new Employee[]{e1, e2, e3, e4, e5, e6, e7, e8, e9, e10}));
 
        System.out.println( filterEmployees(employees, isAdultMale()) );
 
        System.out.println( filterEmployees(employees, isAdultFemale()) );
 
        System.out.println( filterEmployees(employees, isAgeMoreThan(35)) );
 
        //Employees less than or equals to 35
        //can be find using negate()
        System.out.println(filterEmployees(employees, isAgeMoreThan(35).negate()));
    }
}
[1 - 23, 3 - 43, 4 - 26, 8 - 79, 10 - 45]
[5 - 19, 7 - 68]
[3 - 43, 7 - 68, 8 - 79, 10 - 45]
[1 - 23, 2 - 13, 4 - 26, 5 - 19, 6 - 15, 9 - 15]

Predicates are really very good addition in Java 8 and I am going to use it whenever I will get the chance.

3. Conclusion

  • Predicates move the conditions (sometimes business logic) to a central place. This helps in unit-testing them separately.
  • Any code change need not be duplicated into multiple places thus predicates improve the code maintenance.
  • The names predicates such as “isAdultFemale()” are much more readable than writing a if-else block.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

22 thoughts on “Java Predicates”

  1. under TestEmployeePredicates shouldnt it be “EmployeesPredicates.filterEmployees()” and “EmployeesPredicates.isAdultFemale()” (e.g.)?

    Reply
  2. Using predicates will have performance impact on large collection of Objects right… ???

    I have tried filtering out the even numbers from the a list containing 0-1500 using Guava Predicates and normal conventional way of for and if loop implementation .

    I guess it will apply to the Java Se8 Predicate case as well right?

    Reply
    • I also tried to find the difference in performance. You are right it’s much bigger than alternate ways of for loop iterations. Below is code I tried and executed it multiple times with almost same result.

      public static void main(String[] args){
      List list = new ArrayList();
      for(int i = 1; i< 100000; i++){ list.add(i); } long startTime = System.currentTimeMillis(); Stream
      stream = list.stream();
      Integer[] evenNumbersArr = stream.filter(i -> i%2 == 0).toArray(Integer[]::new);
      System.out.println(evenNumbersArr);

      long endTime = System.currentTimeMillis();
      System.out.println(endTime - startTime);

      startTime = System.currentTimeMillis();
      Integer[] evenNumbersArr2 = new Integer[list.size()];
      for(Integer i : list){
      if(i%2 == 0) evenNumbersArr2[i] = i;
      }
      System.out.println(evenNumbersArr2);

      endTime = System.currentTimeMillis();
      System.out.println(endTime - startTime);
      }

      Output:

      [Ljava.lang.Integer;@d46ca6
      57
      [Ljava.lang.Integer;@117d9a3
      3

      Reply
      • This means using streams, slows down the performance atleast in this case. I tried this too, and with traditional approach results are faster.

        Reply
        • With a trivial example like this then yes the overhead of stream processing is probably not worth it. I think that one of the central tenets of streams is that you can chain operations together and they are ‘squashed’ and only evaluated once on the terminal operation, whereas pre-Java8 you would have to loop over the collection each time for each operation. The stream API also gives you the powerful option to parallelise the processing simply by calling .parallel() on an existing stream.

          Reply
          • Streams are useful when they are used parallel for large collections otherwise its an overhead to create streams.
            I also had evaluated the performance for loops and parallel streams win in this case.
            For small collections traditional or Java 5 way is useful.

          • Agree. There is a trade off between clean code and performance gain. For small collections , the performance gain is not that much but the code readability increase many fold. I will go for clean code in this case considering powerful hardware available these days.

  3. Hi,

    In TestEmployeePredicates class,you called filterEmployee() method.where it is there and can you write a Predicate interface.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.