Monday, August 12, 2019

Java 8 Predicate Working Examples

1. Overview


In this tutorial, We'll learn how to work and use the Predicate Functional Interface.

In the previous post, We have discussed "Introduction to Functional Interfaces in Java 8". Explained an easy manner to understand the type of Functional Interfaces.

Predicate Functional interface is part of java.lang.function package.

Predicate is one of the categories of Function Interfaces (Suppliers, Consumers, Predicates and Function)

Below is the definition from Predicate API.

@FunctionalInterface
public interface Predicate<T>

Predicate has a abstract method named test(T t) which takes only one argument and does return a boolean value such as true or false.

This is mainly used to test a condition to perform certain operations.

test(T t) method can be called as Functional Method. Below is the signature.
boolean test(T t)

Predicate has many non-abstract methods apart from test(). Those are two static and three default methods.


Static Methods:

static <T> Predicate<T> isEqual(Object targetRef) 
static <T> Predicate not(Predicate target) 

Default Methods:

default Predicate and(Predicate other) 
default Predicate<T> negate() 
default Predicate<T> or(Predicate other)

We will be discussing all these methods with examples.

In all examples, We will be using the below Student class.

class Student {

int id;
String name;
int age;

public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}

@Override
public boolean equals(Object obj) {

Student other = (Student) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

2. Predicate test(T t) 

This is the function method in the Predicate interface.

2.1 Example 1

This is a simple straight forward example to creating a predicate for student age to check more than 18 years.

Predicate agePredicate = s -> s.getAge() > 18;

Student s1 = new Student(100, "Larry", 25);

boolean is18Plus = agePredicate.test(s1);

if(is18Plus) {
System.out.println("Larray is 18 plus");
}


 Output:

Lary is 18 plus

2.2 Example with List stream

Creating a list of Students.

List stuList = new ArrayList<>();
stuList.add(new Student(100, "Lary", 10));
stuList.add(new Student(200, "page", 20));
stuList.add(new Student(101, "sundar", 30));
stuList.add(new Student(202, "pichai", 17));

Converting list to stream and passing a Predicate to filter method.

List list18Plus = stuList.stream().filter(stu -> stu.getAge() > 18).collect(Collectors.toList());
System.out.println(list18Plus);

Now, This filter will be executed for every student in the stuList and checks the predicate condition. If the condition returns true then collect into List.

Output:

This produces the output with 18+ age students.

[Student [id=100, name=page, age=20], Student [id=100, name=sundar, age=30]]

3. Predicate and(Predicate p) method


Predicate can be used in chaining. That means we can call and method on existing Predicate instance.

Now, We want to add an additional test condition to the existing predicate that name starts with "p".

and() method is used to club two predicates and produces a boolean result. It returns two if and only if two predicate's are matched, Otherwise false.

This is similar to the logical AND and as below.

predicate1.and(prediate2);

3.1 Example 1

Existing predicate:

Predicate agePredicate = s -> s.getAge() > 18;

New Predicate:


Predicate namePredicate = s -> s.getName().startsWith("p");
Predicate ageNamePredicate = agePredicate.and(namePredicate);
Student page = new Student(200, "page", 20);

boolean isMatched = ageNamePredicate.test(page);
System.out.println(isMatched);

 Output:

true

3.2 Example With Streams


List  andResult = stuList.stream().filter(ageNamePredicate).collect(Collectors.toList());
System.out.println(andResult);

 Output:

This produces only one student because there is only one Student with age > 18 and the name starts with "p".

[Student [id=200, name=page, age=20]]

4 Predicate or(Predicate p) method


or(Predicate p) method works the same as logical OR. This method takes another predicate as an argument and returns a boolean value.

Note: This or() method is called on another predicate.
predicate1.or(prediate2);

If anyone of the Predicate's is true then predicate returns true.

See the example with collections and streams.

Creating or() predicate.

Predicate orPredicate = agePredicate.or(namePredicate);

Passing orPredicate to Stream filter() method.

List  orResult = stuList.stream().filter(orPredicate).collect(Collectors.toList());
System.out.println(orResult);

Output:

[Student [id=200, name=page, age=20], Student [id=101, name=sundar, age=30], Student [id=202, name=pichai, age=17]]

5. Predicate isEqual(Object obj)


isEqual method takes Object as an argument. That means we can pass any object to this. This internally calls Objects equals() method.

In this example, we have overridden the equals() method Student class.

isEqual is a static method. So, it can be called directly invoked on Predicate. See the below example.

Creating a student object.

Student sunder = new Student(200, "page", 20);

Creating a predicate with isEqual() method and passing sunder obj to it.

Predicate isEqualPredicate = Predicate.isEqual(page);

Now passing this predicate to Stream filter() method and checks.

List isEqualResult = stuList.stream().filter(isEqualPredicate).collect(Collectors.toList());
System.out.println(isEqualResult);

Output:

[Student [id=200, name=page, age=20]]

6. default Predicate negate()


negate() returns a Predicate that does not satisfy the condition.

Let us write an example program to understand negate() default method.

Predicate namePredicate = s -> s.getName().startsWith("p");
Predicate nameNegatePredicate = namePredicate.negate();
List negateResult = stuList.stream().filter(nameNegatePredicate).collect(Collectors.toList());
System.out.println(negateResult);

Output:

[Student [id=100, name=Lary, age=10], Student [id=101, name=sundar, age=30]]

First, written the predicate to find the student name starts with 'p'
Next, invoked the negate() on it and returned a new predicate.
Finally, Passed negate to the filter method and then collected the students whose names not started with 'p'. Observe the above output.

7. Predicate not(Predicate target)


not() method is newly introduced as part of Java 11 API. This works similar to the negate() method but it does take the Predicate as an argument.

And also this is a static method so we can access the class name directly. not() method uses the target.negate() method internally.

Now it time to an example program on not() function.

Predicate agePredicate = s -> s.getAge() > 18;
Predicate ageNegatePredicate = Predicate.not(agePredicate);
List notResult = stuList.stream().filter(ageNegatePredicate).collect(Collectors.toList());
System.out.println(notResult);

Output:

[Student [id=100, name=Lary, age=10], Student [id=202, name=pichai, age=17]]

8. NullPointerException


We should be knowing while using Predicate methods such as and(..), isEqual(..), or(..) and not(..) take a predicate as arguments. If we pass null then it will throw Runtime NullPointerException.

9. ArrayList.removeif(Predicate p)


Arraylist API has a method removeif().

Removes all of the elements of this collection that satisfy the given predicate. Errors or runtime exceptions thrown during the iteration or by the predicate are relayed to the caller.

public boolean removeIf(Predicate filter)

This returns boolean, true - if any elements removed. false - otherwise.

Example:

System.out.println("Student list size before removal : "+stuList.size());
boolean removed = stuList.removeIf( s -> s.getId() == 100);
System.out.println(removed);
System.out.println("Student list size after removal : "+stuList.size());

Output:

Student list size before removal : 4
true
Student list size after removal : 3

10. Conclusion


In this detailed post, We've seen what is Predicate Functional Interface and its methods.

Example of each method and how to use them.

Predicate is widely used in Java 8 Stream api and should be used with Lambda Expressions.

And also seen the removeIf() method in the ArrayList API that uses Predicate.

All the code shown in this post is over GitHub.

No comments:

Post a Comment