Java BiPredicate Interface
Last modified: April 16, 2025
The java.util.function.BiPredicate
interface represents a predicate
that takes two arguments and returns a boolean result. It is a functional
interface with a single abstract method test
. BiPredicate is used
for evaluating conditions against two input values.
BiPredicate
was introduced in Java 8 as part of the functional
programming utilities. It enables testing conditions with two parameters in a
functional style. The interface provides default methods for logical operations
like AND, OR, and negation.
BiPredicate Interface Overview
BiPredicate
interface contains one abstract method and three default
methods. The key method test
evaluates the condition. Other methods
enable predicate composition with logical operations.
@FunctionalInterface public interface BiPredicate<T, U> { boolean test(T t, U u); default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other); default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other); default BiPredicate<T, U> negate(); }
The code above shows the structure of BiPredicate
interface. It uses
generics where T and U are input types. The interface is annotated with
@FunctionalInterface to indicate its single abstract method nature.
Basic BiPredicate Usage
The simplest way to use BiPredicate is with lambda expressions. We define the condition to test against two inputs. The example checks if one string contains another.
package com.zetcode; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { // Define a BiPredicate that checks if first string contains second BiPredicate<String, String> containsChecker = (str, substr) -> str.contains(substr); // Test the predicate System.out.println("Contains check: " + containsChecker.test("hello world", "hello")); System.out.println("Contains check: " + containsChecker.test("java", "python")); // Using method reference BiPredicate<String, String> equalsChecker = String::equals; System.out.println("Equals check: " + equalsChecker.test("test", "test")); } }
This example demonstrates basic BiPredicate usage with lambda and method reference. The containsChecker takes two strings and returns boolean. We test different string pairs. Method reference provides concise syntax for existing methods.
Combining BiPredicates with AND
The and
method allows combining two predicates with logical AND.
Both predicates must evaluate to true for the combined predicate to return true.
package com.zetcode; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { // First predicate checks if first number is greater than second BiPredicate<Integer, Integer> greaterThan = (a, b) -> a > b; // Second predicate checks if difference is less than 10 BiPredicate<Integer, Integer> diffLessThan10 = (a, b) -> Math.abs(a - b) < 10; // Combine with AND BiPredicate<Integer, Integer> combined = greaterThan.and(diffLessThan10); System.out.println("5 > 3 and diff < 10: " + combined.test(5, 3)); System.out.println("15 > 3 and diff < 10: " + combined.test(15, 3)); } }
This example shows predicate combination with and
. The combined
predicate checks both conditions. First test passes (5>3 and difference 2<10).
Second test fails (15>3 but difference 12 not <10).
Combining BiPredicates with OR
The or
method combines two predicates with logical OR. Either
predicate evaluating to true makes the combined predicate return true.
package com.zetcode; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { // Check if strings have same length BiPredicate<String, String> sameLength = (s1, s2) -> s1.length() == s2.length(); // Check if strings start with same letter BiPredicate<String, String> sameFirstLetter = (s1, s2) -> s1.charAt(0) == s2.charAt(0); // Combine with OR BiPredicate<String, String> combined = sameLength.or(sameFirstLetter); System.out.println("'apple' and 'orange': " + combined.test("apple", "orange")); System.out.println("'banana' and 'berry': " + combined.test("banana", "berry")); } }
This example demonstrates or
combination. First test passes because
both strings start with 'a' (even though lengths differ). Second test passes
because lengths are equal (even though starting letters differ).
Negating a BiPredicate
The negate
method returns a predicate representing the logical
negation of the original predicate. It inverts the boolean result.
package com.zetcode; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { // Check if number is divisible by another BiPredicate<Integer, Integer> isDivisible = (num, divisor) -> num % divisor == 0; // Create negated version BiPredicate<Integer, Integer> isNotDivisible = isDivisible.negate(); System.out.println("10 divisible by 5: " + isDivisible.test(10, 5)); System.out.println("10 not divisible by 3: " + isNotDivisible.test(10, 3)); } }
This example shows predicate negation. The original predicate checks divisibility. The negated version returns true when numbers are not divisible. Negation is useful for inverting conditions without rewriting them.
Using BiPredicate with Collections
BiPredicate can be used with collections to implement complex filtering logic. The example filters map entries based on both key and value conditions.
package com.zetcode; import java.util.HashMap; import java.util.Map; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 85); scores.put("Bob", 92); scores.put("Charlie", 78); scores.put("David", 65); // Filter names starting with A-C and scores >= 80 BiPredicate<String, Integer> filter = (name, score) -> name.charAt(0) <= 'C' && score >= 80; System.out.println("Filtered entries:"); scores.forEach((name, score) -> { if (filter.test(name, score)) { System.out.println(name + ": " + score); } }); } }
This example demonstrates BiPredicate with collections. We filter map entries where names start with A-C and scores are 80+. The predicate evaluates both key and value conditions together. This approach is cleaner than separate filters.
BiPredicate in Stream Filtering
BiPredicate can be adapted for stream filtering by converting it to a Predicate. This is useful when stream elements contain multiple values to test.
package com.zetcode; import java.util.List; import java.util.function.BiPredicate; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { record Person(String name, int age) {} List<Person> people = List.of( new Person("Alice", 25), new Person("Bob", 30), new Person("Charlie", 20), new Person("David", 35) ); // BiPredicate to check name length and age BiPredicate<String, Integer> filter = (name, age) -> name.length() > 3 && age >= 25; // Convert BiPredicate to Predicate for stream filter List<Person> filtered = people.stream() .filter(p -> filter.test(p.name(), p.age())) .collect(Collectors.toList()); System.out.println("Filtered people:"); filtered.forEach(p -> System.out.println(p.name() + " - " + p.age())); } }
This example shows BiPredicate in stream operations. We filter Person objects based on name length and age. The lambda adapts BiPredicate to Predicate for use in filter. This maintains clean separation of filtering logic.
Chaining Multiple BiPredicates
BiPredicates can be chained together for complex condition evaluation. This example checks multiple conditions on product price and category.
package com.zetcode; import java.util.function.BiPredicate; public class Main { public static void main(String[] args) { // Check if product is in electronics category BiPredicate<String, Double> isElectronics = (category, price) -> category.equals("Electronics"); // Check if price is in premium range BiPredicate<String, Double> isPremiumPrice = (category, price) -> price > 500; // Check if product is on sale BiPredicate<String, Double> isOnSale = (category, price) -> price < 100; // Complex condition: (Electronics AND premium) OR on sale BiPredicate<String, Double> complexCondition = isElectronics.and(isPremiumPrice).or(isOnSale); System.out.println("Laptop $600: " + complexCondition.test("Electronics", 600.0)); System.out.println("Book $80: " + complexCondition.test("Books", 80.0)); System.out.println("TV $400: " + complexCondition.test("Electronics", 400.0)); } }
This example demonstrates complex predicate chaining. The condition evaluates to true for electronics priced over $500 OR any product under $100. Chaining predicates creates readable complex conditions from simple components.
Source
Java BiPredicate Interface Documentation
In this article, we've covered the essential methods and features of the Java BiPredicate interface. Understanding these concepts is crucial for functional programming with two-argument conditions in modern Java applications.
Author
List all Java tutorials.