Java Joy: Combining Predicates
In Java we can use a Predicate
to test if something is true
or false
. This is especially useful when we use the filter
method of the Java Stream API. We can use lambda expressions to define our Predicate
or implement the Predicate
interface. If we want to combine different Predicate
objects we can use the or
, and
and negate
methods of the Predicate
interfaces. These are default methods of the interface and will return a new Predicate
.
Let’s start with an example where we have a list of String
values. We want to filter all values that start with Gr or with M. In our first implementation we use a lambda expression as Predicate
and implements both tests in this expression:
package mrhaki;
import java.util.List;
import java.util.stream.Collectors;
public class PredicateComposition1 {
public static void main(String[] args) {
final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");
final List<String> gr8Stuff =
items.stream()
// Use lambda expression with both tests as Predicate.
.filter(s -> s.startsWith("Gr") || s.startsWith("M"))
.collect(Collectors.toUnmodifiableList());
assert gr8Stuff.size() == 4 : "gr8Stuff contains 4 items";
assert gr8Stuff.contains("Groovy");
assert gr8Stuff.contains("Gradle");
assert gr8Stuff.contains("Grails");
assert gr8Stuff.contains("Micronaut");
}
}
We will rewrite the previous example and introduce the startsWith
method that returns a new Predicate
. Then in our filter
method we use the or
method of the Predicate
object to combine the two Predicate
objects:
package mrhaki;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PredicateComposition2 {
public static void main(String[] args) {
final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");
final List<String> gr8Stuff =
items.stream()
// Use the Predicate.or method to combine two Predicate objects.
.filter(startsWith("Gr").or(startsWith("M")))
.collect(Collectors.toUnmodifiableList());
assert gr8Stuff.size() == 4 : "gr8Stuff contains 4 items";
assert gr8Stuff.contains("Groovy");
assert gr8Stuff.contains("Gradle");
assert gr8Stuff.contains("Grails");
assert gr8Stuff.contains("Micronaut");
}
// Create a predicate to check if String value starts with a given value.
private static Predicate<String> startsWith(final String begin) {
return s -> s.startsWith(begin);
}
}
In the following example we use the negate
and and
method to find all values that do not start with Gr and with a length less than 8 characters:
package mrhaki;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PredicateComposition3 {
public static void main(String[] args) {
final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");
final List<String> otherStuff =
items.stream()
// Find all values that do not start with "Gr"
// and have less than 8 characters.
.filter(startsWith("Gr").negate().and(smallerThan(8)))
.collect(Collectors.toUnmodifiableList());
assert otherStuff.size() == 2 : "otherStuff contains 2 items";
assert otherStuff.contains("Java");
assert otherStuff.contains("Kotlin");
}
// Create a predicate to check if String value starts with a given value.
private static Predicate<String> startsWith(final String begin) {
return s -> s.startsWith(begin);
}
// Create a predicate to check if String value has
// less characters than the given size.
private static Predicate<String> smallerThan(final int size) {
return s -> s.length() < size;
}
}
In our previous example we can replace the negate
method call on our predicate with the static Predicate.not
method. The predicate is than an argument and is just another way to express the same predicate:
package mrhaki;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PredicateComposition4 {
public static void main(String[] args) {
final var items = List.of("Groovy", "Gradle", "Grails", "Micronaut", "Java", "Kotlin");
final List<String> otherStuff =
items.stream()
// Find all values that do not start with "Gr",
// using Predicate.not instead of negate,
// and have less than 8 characters.
.filter(Predicate.not(startsWith("Gr")).and(smallerThan(8)))
.collect(Collectors.toUnmodifiableList());
assert otherStuff.size() == 2 : "otherStuff contains 2 items";
assert otherStuff.contains("Java");
assert otherStuff.contains("Kotlin");
}
// Create a predicate to check if String value starts with a given value.
private static Predicate<String> startsWith(final String begin) {
return s -> s.startsWith(begin);
}
// Create a predicate to check if String value has
// less characters than the given size.
private static Predicate<String> smallerThan(final int size) {
return s -> s.length() < size;
}
}
Written with Java 12.