Java 8 Predicate Chain

1. Overview

In this quick tutorial, we’ll discuss different ways to chain Predicates in Java 8.

2. Basic Example

First, let’s see how to use a simple Predicate to filter a List of names:

@Test
public void whenFilterList_thenSuccess(){
   List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom");
   List<String> result = names.stream()
     .filter(name -> name.startsWith("A"))
     .collect(Collectors.toList());
   
   assertEquals(2, result.size());
   assertThat(result, contains("Adam","Alexander"));
}

In this example, we filtered our List of names to only leave names that start with “A” using the Predicate:

name -> name.startsWith("A")

But what if we wanted to apply multiple Predicates?

3. Multiple Filters

If we wanted to apply multiple Predicates, one option is to simply chain multiple filters:

@Test
public void whenFilterListWithMultipleFilters_thenSuccess(){
    List<String> result = names.stream()
      .filter(name -> name.startsWith("A"))
      .filter(name -> name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

We’ve now updated our example to filter our list by extracting names that start with “A” and have a length that is less than 5.

We used two filters — one for each Predicate.

4. Complex Predicate

Now, instead of using multiple filters, we can use one filter with a complex Predicate:

@Test
public void whenFilterListWithComplexPredicate_thenSuccess(){
    List<String> result = names.stream()
      .filter(name -> name.startsWith("A") && name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

This option is more flexible than the first one, as we can use bitwise operations to build the Predicate as complex as we want.

5. Combining Predicates

Next, if we don’t want to build a complex Predicate using bitwise operations, Java 8 Predicate has useful methods that we can use to combine Predicates.

We’ll combine Predicates using the methods Predicate.and()Predicate.or(), and Predicate.negate().

5.1. Predicate.and()

In this example, we’ll define our Predicates explicitly, and then we’ll combine them using Predicate.and():

@Test
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("A");
    Predicate<String> predicate2 =  str -> str.length() < 5;
  
    List<String> result = names.stream()
      .filter(predicate1.and(predicate2))
      .collect(Collectors.toList());
        
    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

As we can see, the syntax is fairly intuitive, and the method names suggest the type of operation. Using and(), we’ve filtered our List by extracting only names that fulfill both conditions.

5.2. Predicate.or()

We can also use Predicate.or() to combine Predicates.

Let’s extract names start with “J”, as well as names with a length that’s less than 4:

@Test
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("J");
    Predicate<String> predicate2 =  str -> str.length() < 4;
    
    List<String> result = names.stream()
      .filter(predicate1.or(predicate2))
      .collect(Collectors.toList());
    
    assertEquals(2, result.size());
    assertThat(result, contains("John","Tom"));
}

5.3. Predicate.negate()

We can use Predicate.negate() when combining our Predicates as well:

@Test
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("J");
    Predicate<String> predicate2 =  str -> str.length() < 4;
    
    List<String> result = names.stream()
      .filter(predicate1.or(predicate2.negate()))
      .collect(Collectors.toList());
    
    assertEquals(3, result.size());
    assertThat(result, contains("Adam","Alexander","John"));
}

Here, we’ve used a combination of or() and negate() to filter the List by names that start with “J” or have a length that isn’t less than 4.

5.4. Combine Predicates Inline

We don’t need to explicitly define our Predicates to use and(),  or(), and negate().

We can also use them inline by casting the Predicate:

@Test
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
    List<String> result = names.stream()
      .filter(((Predicate<String>)name -> name.startsWith("A"))
      .and(name -> name.length()<5))
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

6. Combining a Collection of Predicates

Finally, let’s see how to chain a collection of Predicates by reducing them.

In the following example, we have a List of Predicates that we combined using Predicate.and():

@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
    List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
    allPredicates.add(str -> str.startsWith("A"));
    allPredicates.add(str -> str.contains("d"));        
    allPredicates.add(str -> str.length() > 4);
    
    List<String> result = names.stream()
      .filter(allPredicates.stream().reduce(x->true, Predicate::and))
      .collect(Collectors.toList());
    
    assertEquals(1, result.size());
    assertThat(result, contains("Alexander"));
}

Note that we use our base identity as:

x->true

But that will be different if we want to combine them using Predicate.or():

@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
    List<String> result = names.stream()
      .filter(allPredicates.stream().reduce(x->false, Predicate::or))
      .collect(Collectors.toList());
    
    assertEquals(2, result.size());
    assertThat(result, contains("Adam","Alexander"));
}

7. Conclusion

In this article, we explored different ways to chain Predicates in Java 8, by using filter(), building complex Predicates, and combining Predicates.

The full source code is available over on GitHub.

Related posts:

Read an Outlook MSG file
Migrating from JUnit 4 to JUnit 5
Java Program to Construct an Expression Tree for an Infix Expression
Hướng dẫn tạo và sử dụng ThreadPool trong Java
Java Program to Find kth Smallest Element by the Method of Partitioning the Array
HttpClient Basic Authentication
Java Program to Implement Levenshtein Distance Computing Algorithm
A Guide to Spring Cloud Netflix – Hystrix
Generating Random Numbers in a Range in Java
Request a Delivery / Read Receipt in Javamail
Java Program to Perform Insertion in a 2 Dimension K-D Tree
Returning Image/Media Data with Spring MVC
Java Program to Implement LinkedHashMap API
Một số tính năng mới về xử lý ngoại lệ trong Java 7
Spring Webflux and CORS
DynamoDB in a Spring Boot Application Using Spring Data
Java Program to Apply DFS to Perform the Topological Sorting of a Directed Acyclic Graph
Hướng dẫn Java Design Pattern – Factory Method
Guava – Join and Split Collections
Lập trình đa luồng với Callable và Future trong Java
Intro to the Jackson ObjectMapper
A Guide to the ResourceBundle
A Guide to WatchService in Java NIO2
Hướng dẫn sử dụng Printing Service trong Java
Logging in Spring Boot
Pagination and Sorting using Spring Data JPA
Setting a Request Timeout for a Spring REST API
Java Program to Implement Heap Sort Using Library Functions
Java InputStream to String
Adding Parameters to HttpClient Requests
Guide to Selenium with JUnit / TestNG
Java Program to Perform Searching Based on Locality of Reference