Java 8 Streams – anyMatch, allMatch, and noneMatch

Introduction

The anyMatchallMatch and noneMatch methods are short-circuiting terminal operations on a Java Stream.In this post we will learn about the Java 8 Stream’s anyMatch, allMatch and noneMatch methods.

AnyMatch, AllMatch, and NoneMatch

Let us look at the three methods – anyMatch, allMatch and noneMatch and their signatures.

AnyMatch

boolean anyMatch(Predicate<? super T> predicate)

We use this terminal method to find if any elements of the stream (at least one) match the provided predicate. It is  a short-circuiting because it does not have to evaluate the predicate on all the elements. Example: If the third element satisfies the predicate, then the rest of the stream elements will not be processed. On the other hand, if none of the elements satisfy the predicate, then the entire stream will have to be processed.

AllMatch

boolean allMatch(Predicate<? super T> predicate)

The allMatch method returns whether all the elements of the stream match the passed predicate. It need not evaluate the predicate on all the elements (say, the first element itself fails to satisfy the predicate). But it has to apply the predicate to all the elements when it returns true.

NoneMatch

boolean noneMatch(Predicate<? super T> predicate)

Finally, the noneMatch method returns whether no elements of the stream match the predicate. It will not evaluate the predicate on all the elements as it will return false if it finds an element that satisfies the predicate. It will process the entire stream when it returns true (when none of the elements match the predicate).

Examples

For the examples, I will use the below shown Student and Course POJOs.

public class Student {
    private long id;
    private String name;
    private int age;
    private List<Course> courses;

    public Student(long id, String name, int age, List<Course> courses) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.courses = Collections.unmodifiableList(courses);
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public List<Course> getCourses() {
        return courses;
    }
}

Notice the defensive copying of the passed list of courses using Collections.unmodifiableList. This will make the Student class immutable (See the Benefits an immutable class has).

public class Course {
    private int courseId;
    private String name;

    public Course(int courseId, String name) {
        this.courseId = courseId;
        this.name = name;
    }

    public int getCourseId() {
        return courseId;
    }

    public String getName() {
        return name;
    }

    @Override
    public int hashCode() {
        return Objects.hash(courseId);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Course other = (Course) obj;
        return Objects.equals(courseId, other.courseId); //We consider two courses to be equal if they have the same course id
    }
}

Let us create a list of students where each student has a name, an id, age and studies a list of courses. 

Course computerScience = new Course(1, "Computer Science");
Course physics = new Course(2, "Physics");
Course chemistry = new Course(3, "Chemistry");
Course mathematics = new Course(4, "Mathematics");
Course discreteMathematics = new Course(5, "Discrete Mathematics");
Course computerArchitecture = new Course(6, "Computer Architecture");

List<Student> students = List.of(
        new Student(1, "Kelly", 19, List.of(physics, chemistry, mathematics)),
        new Student(2, "Jon", 20, List.of(computerScience, computerArchitecture, mathematics)),
        new Student(3, "Joseph", 19, List.of(computerScience, mathematics, discreteMathematics)),
        new Student(4, "Adam", 20, List.of(computerScience, mathematics))
);

Examples of anyMatch

Let us find if the physics course has been taken by any student at all.

Predicate<Student> studentHasPhysicsCourse = student -> student.getCourses().contains(physics);
System.out.println(students.stream()
        .anyMatch(studentHasPhysicsCourse)); //true

We first create a stream of students and pass the predicate studentHasPhysicsCourse to the anyMatch method. It returns true when it evaluates the predicate for the first student. The rest of the stream elements will be skipped.

Let us look at a negative case where none of the elements match the predicate.

Check if there are any students who is more than 20 years of age.
Predicate<Student> studentAgeMoreThan20 = student -> student.getAge() > 20;
System.out.println(students.stream()
        .anyMatch(studentAgeMoreThan20)); //false

In this case, it would have processed all the elements in the stream before returning false.

Let us look at another example (slightly complicated). Let us check if there is any student who has taken a course that starts with ‘Computer’. The predicate is shown below:

Predicate<Student> hasCourseNameStartingWithComputer = student -> student.getCourses().stream()
        .anyMatch(course -> course.getName().startsWith("Computer"));

The predicate itself has anyMatch operation applied to the stream of courses.

System.out.println(students.stream()
        .anyMatch(hasCourseNameStartingWithComputer)); //true

Short-circuiting behaviour of anyMatch

Let us add a print statement to the stream pipeline to verify the short-circuiting behaviour of the anyMatch method.

System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .anyMatch(hasCourseNameStartingWithComputer)); //true

Prints,

Processing Student: Kelly
Processing Student: Jon
true

When it was processing the second student, the predicate evaluated to true. Hence, it didn’t process the rest of the elements of the stream.

Let us check the same for a negative scenario. Let us check if there is a student whose course name starts with ‘Project’

Predicate<Student> hasCourseNameStartingWithProject = student -> student.getCourses().stream()
        .anyMatch(course -> course.getName().startsWith("Project"));

System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .anyMatch(hasCourseNameStartingWithProject)); //false

Prints,

Processing Student: Kelly
Processing Student: Jon
Processing Student: Joseph
Processing Student: Adam
false

Since none of the students will satisfy that predicate, it had to process all the students before returning false.

Examples of allMatch

Predicate used: If a student is less than 21 years.
We pass this predicate to the allMatch method. Since all the students are under 21 years of age, it evaluated to true.

Predicate<Student> studentAgeLessThan21 = student -> student.getAge() < 21;
System.out.println(students.stream()
        .allMatch(studentAgeLessThan21)); //true

Another example: Check if all the students have the mathematics course.

Predicate<Student> studentHasMathCourse = student -> student.getCourses().contains(mathematics);
System.out.println(students.stream()
        .allMatch(studentHasMathCourse)); //true

Similarly, let us check if all the students have CS course. 

Predicate<Student> studentHasCSCourse = student -> student.getCourses().contains(computerScience);
System.out.println(students.stream()
        .allMatch(studentHasCSCourse)); //false

Check if all the students have at least one course that starts with ‘Computer’.

Predicate<Student> hasCourseNameStartingWithComputer = student -> student.getCourses().stream()
        .anyMatch(course -> course.getName().startsWith("Computer"));
System.out.println(students.stream()
        .allMatch(hasCourseNameStartingWithComputer)); //false

As before, the predicate construction uses a stream with anyMatch.

Short-circuiting behaviour of allMatch

Let us see how allMatch does short-circuiting of the stream processing by adding a peek statement.

System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .allMatch(hasCourseNameStartingWithComputer)); //true

Outputs,

Processing Student: Kelly
false

The first student Kelly does not have a Computer Science course. Hence the predicate failed, and it returned false. The rest of the elements weren’t processed.

When all the elements satisfy the predicate, it would evaluate the predicate against all the elements. Let us go back to the first example (check if students are less than 21).

System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .allMatch(studentAgeLessThan21)); //true

Outputs,

Processing Student: Kelly
Processing Student: Jon
Processing Student: Joseph
Processing Student: Adam
true

Examples of noneMatch

Checking if none of the students are less than 21 years of age.

Predicate<Student> studentAgeLessThan21 = student -> student.getAge() < 21;
System.out.println(students.stream()
        .noneMatch(studentAgeLessThan21)); //false

Checking if none of the students are more than 21 years of age.

Predicate<Student> studentAgeMoreThan21 = student -> student.getAge() > 21;
System.out.println(students.stream()
        .noneMatch(studentAgeMoreThan21)); //true

Short-circuiting behaviour of noneMatch

Let us check if none of the students took computer science.

Predicate<Student> studentHasTakenComputerScience = student -> student.getCourses().contains(computerScience);
System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .noneMatch(studentHasTakenComputerScience)); //false

Prints,

Processing Student: Kelly
Processing Student: Jon
false

The first student didn’t take the CS course, and the predicate was false and hence it continued to the next element in the stream. The second student has the CS course, and the predicate evaluated to true. This caused the noneMatch to return false, and the rest of the elements weren’t processed.

On the other hand, if all the elements failed to satisfy the predicate, it will process all the elements before returning true (Note: We are using noneMatch and hence it returns true only if all the elements failed to match the predicate).

Predicate<Student> studentAgeMoreThan20 = student -> student.getAge() > 20;
System.out.println(students.stream()
        .peek(student -> System.out.println("Processing Student: " + student.getName()))
        .noneMatch(studentAgeMoreThan20)); //true

Prints,

Processing Student: Kelly
Processing Student: Jon
Processing Student: Joseph
Processing Student: Adam
true

Conclusion

In this post we learnt about the Java 8 stream’s anyMatch, allMatch and the noneMatch methods. We looked at the method signatures and what each method does with examples. We also saw the short-circuiting behaviour for each of the methods.

Leave a Reply