Matches in Java Stream

Java Stream: Predicate Methods

Gene Zeiniss
4 min readDec 27, 2019

The policy of the company I work for is to review the code before pushing it to master. By reviewing my peers' code, I noticed that most of the developers are using very few Java stream operations (the most popular are filter() and map()).

So I asked myself, why other stream functions are neglected by my co-workers? The obvious answer was: They are not familiar with Java stream features. And if they are, maybe my colleagues don’t feel comfortable using other methods.

One way or another, I decided to write a series of posts about Java stream operations, and this is my two cents on the topic.

This is the first post of the series, which will deal with Java stream predicate methods: anyMatch(), allMatch() and noneMatch(). I call this group “matches”.

I will introduce the “matches” operations while trying to understand the benefits of one method over another, and which one is better to use in what situation.

First a few words about the Java stream.

Stream in Java

The stream is one of the major functionality, introduced in Java 8. In general, is a collection of objects that supports various methods, which can be pipelined to produce the desired result.

The stream characteristics:

  • The sequence of elements − stream provides a set of elements of a specific type in a sequential manner.
  • Aggregate operations − stream supports aggregate operations. The operations that can be performed on a stream are divided into intermediate operations (returns stream) and terminal operations (return a result of a definite type).
  • Pipelining — each intermediate operation is lazily executed and returns a stream as a result, hence can be pipelined.
  • Short-circuiting — terminal operations mark the end of the stream and return the result.
  • Stream is not data structure — a stream does not store data and, in that sense, is not a data structure.
  • The operations on streams don’t change the original source.

In this post, we will tackle the “matches”; terminal operations, that take a predicate and return a boolean.

Predicate

Predicate, in general meaning, is a statement about something that is either true or false. In Java, a predicate is just a function, that represents a predefined condition.

Given a stream of objects, often we need to check whether objects match specific criteria. Once you provided a predefined predicate as an input to the matching method, then Java processes the function internally and returns the boolean result, whether a match for the condition was found or not.

Java provides such declarative matching with predicate conditions using three methods defined on the streams: anyMatch(Predicate predicate), allMatch(Predicate predicate) and noneMatch(Predicate predicate).

.anyMatch(Predicate predicate)

The anyMatch() method, as suggested by its name, returns true if at least one (any) element in the stream matches the provided predicate, otherwise false. The false is also returned if the stream is empty. In this case, the predicate is not evaluated.

The predicate’s evaluation is terminated as soon as found an element that matches the provided condition. It means the predicate will not be evaluated on all stream’s elements if not necessary for determining the result.

The Contact class will serve us with examples

and email confirmation predicate

Let’s say, we have a stream of contacts, when only one of them has “email confirmed on” value.

By the way, for testing, I’m using javafaker library, which generates a real-looking data

We really want to know: Did anyone in the group confirm their email? In this case, we can just run anyMatch() the method with emailConfirmationPredicate() on contacts stream.

The isAnyContactConfirmedEmail() will return true.

.allMatch(Predicate predicate)

The allMatch() method, unlike anyMatch(), returns true if called stream totally matches the provided predicate. If at least one of the elements doesn’t match the predicate condition, then the method skips the testing of the remaining elements and returns false as a result, which is not surprising.

What was surprising for me is that allMatch() returns true if the stream is empty, without evaluating the predicate.

When trying to apply allMatch() on an empty stream (Stream.empty()), IntelliJ suggests simplifying it to true. And it’s an off-the-go option to check how the method is handled with empty streams.

For comparison, let’s run allMatch() method with same emailConfirmationPredicate() on a stream of contacts, that was created previously.

The method will return false, of course, since only one contact has “email confirmed on”.

.noneMatch(Predicate predicate)

Last but not least, noneMatch() the method is pretty much opposite to allMatch(). In the same manner as allMatch(), noneMatch() applies to total stream. The catch is, that it returns true only when no element matches the provided predicate.

Similarly to the previous methods, noneMatch() is a short-circuiting method, and predicate evaluation will stop once an element that matches the condition is found (to mention, we’re making sure that all the elements in the stream don’t fit).

Despite the oppositeness between allMatch() and noneMatch(), both return true on an empty stream. That’s the point you have to take into account when deciding which method to use.

Run the method. Some dramatic music in the background. The result is… wait for it… false! Of course, it is false — one contact in the group did confirm the email, and it matches emailConfirmationPredicate().

My Conclusion

Well, the bottom line, is I didn’t find the advantage of one “matches” method over the other, at all. All three methods’ evaluation stops once a result is found. It means that the cost is pretty much the same.

anyMatch() method slightly differs from others, because it’s not total. allMatch() and noneMatch() methods are almost completely opposite.

As a matter of fact, it seems like the decision either to use anyMatch(), allMatch() or noneMatch() is equal to the decision of whether to use positive or negative conditions. And if you ask me, I always prefer the positive flow 😎

--

--

Gene Zeiniss

My blog is about wide aspects of programming, from design to code review. Still, I have a predilection for coding, all comes from my unabashed love for Java ☕