Java Joy: Combining Predicates
Learn the joy of combining Predicates in Java.
Join the DZone community and get the full member experience.
Join For FreeIn 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.
You may also like: Towards More Functional Java Using Lambdas as Predicates
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 -> size >= s.length();
}
}
In our previous example, we can replace the negate
method call on our predicate with the static Predicate.not
method. The predicate is then 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 -> size >= s.length();
}
}
Written with Java 12.
Further Reading
Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments