Predicate Negation

In this article, we already discussed how we can use predicates, when we need to evaluate a condition on a group of values.

For example, we can use the below predicate along with the filter() method of Stream to filter out odd numbers.

Stream<Integer> evenIntegers = Stream.of(1, 2, 3, 4, 5).filter(((Predicate<Integer>) c -> c % 2 == 0));
evenIntegers.forEach(System.out::print);

The output of the above code is 2 4.

Here, the predicate is implemented using a lambda expression c -> c% 2 ==0 to identify even numbers, so that the other numbers are filtered out.

How do we negate a predicate?

There are two methods in the Predicate interface that can be used to negate a Predicate.

1. default Predicate<T> negate()
2. static <T> Predicate<T> not​(Predicate<? super T> target)

negate() method

This default method negate() returns a predicate that represents the logical negation of this predicate. Let us see how the negate() method can be used in the first example, to filter out even numbers.

Stream<Integer> oddIntegers = Stream.of(1, 2, 3, 4, 5).filter(((Predicate<Integer>) c -> c % 2 == 0).negate());
oddIntegers.forEach(System.out::print);

The output of this code is 1 3 5. Here, the negate() method is invoked on the predicate, so that odd numbers are returned.

not() method

This static method returns a predicate that is the negation of the supplied predicate. This is accomplished by returning the result of calling target.negate(). Now, let us see the effect of invoking the not() method, as shown in the example.

Predicate<Integer> predicate = c -> c % 2 == 0;
Stream<Integer> oddIntegers = Stream.of(1, 2, 3, 4, 5).filter(Predicate.not(predicate));
oddIntegers.forEach(System.out::print);

This code also prints 1 3 5. In this case, the not() method is invoked statically on the Predicate interface, so that odd numbers are returned.

Using Predicate.not() with Method References

As we already saw in this article, method references enable us to pass references to methods or constructors using the double colon(::) operator.

Next, let us see how we can apply negation in cases where predicate is used with method references. Here is a class Student, with an instance variable grade.

class Student {
	char grade;

	public Student(char grade) {
		this.grade = grade;
	}

	public boolean hasFailed() {
		if (grade == 'F') {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public String toString() {
		return "Student [grade=" + grade + ", toString()=" + super.toString() + "]";
	}
}

Assume that we want to retrieve all the failed students (students who got ‘F’ grade) from the below list.

List<Student> students1 = Arrays.asList(new Student('A'), new Student('B'), new Student('F'));

To achieve that, we can use predicates with filters as shown below.

List<Student> failedStudents = students1.stream()
	.filter(Student::hasFailed)  
	.collect(Collectors.toList());
failedStudents.forEach(System.out::println);

Now, what if we want to retrieve all the passed students instead?

List<Student> passedStudents = students1.stream()
	.filter(Predicate.not(Student::hasFailed))  
	 .collect(Collectors.toList());
passedStudents.forEach(System.out::println);

As shown in the above code, we can leverage the Predicate.not() method in order to maintain usage of method references.

References

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html#negate()

http://talks.skilltoz.com/using-predicates-in-java/

http://talks.skilltoz.com/method-references/

Leave a Reply

Your email address will not be published. Required fields are marked *