Want to know if any object in your List matches a specific criteria? If so, then turn that List into a Stream and use anyMatch().

In this guide, I'll show you how to do just that.

And I'll do it with a real-world example of data from a business application. You know, like the kind of application you're probably working on right now.

So without further ado, let's get started.

The CRM App

Let's say you're building a CRM app. It does the normal stuff that CRM apps do: tracks activities between sales reps and contacts.

Those activities get stored in a MongoDB database as documents. Each document persists info like the title of the activity as well as the type, outcome, location, start time, end time, notes, and the contact involved.

As it stands right now, if you retrieve all the documents from the activities collection, the resulting data set looks like the JSON dump at this link.

As you can see, we're not messing around here. You're going to be working with real-world data.

On the Java side, the Activity class with its related classes mimic the data set that you see above. You can see examples of those classes over on GitHub.

So you can just do a findAll() on that collection above and get a List of Java objects that represent that JSON output. Then, you can use a Java Stream to filter, find, and map as you see fit.

That's what you'll do in this guide.

And, yes, you could do that kind of stuff with MongoDB aggregations. But you're not here to learn about aggregations are you?

Type Safety

Let's say you just added "Phone Call" as an activity type in your CRM app. You've told the sales reps that they can use it and they told you that, yes it works and they've been using it.

But you want to be absolutely sure that it's working.

So here's what you're going to do: you're going to get all the activities from the MongoDB collection and check to see if any of them are using "Phone Call" as an activity type.

If any of them are using it, then you know that your code works as expected. But if you can't find any references to "Phone Call" anywhere, then something has gone horribly wrong.

And you'll need to look for somebody to blame (other than yourself).

So let's take a look at how to implement that solution.

Gently Down the Stream

When you get all the activities from the MongoDB collection, it gets returned as a List thanks to the Spring Boot framework you're currently using.

But you can't do Stream operations on a list.

Therefore, you need to convert that List to a Stream. And that's pretty easy.

Because, you see, the List implementation has a stream() method that does that very thing.

Once you've converted your List to a Stream, you can perform all sorts of Stream-related operations. And you can save yourself lines of code in the process.

Tighter code is a good thing.

So with that intro out of the way, here's the code you've been waiting for:

        List<Activity> activities = activityRepo.findAll();
        
        boolean typeExists = activities
                                .stream()
                                .anyMatch(activity -> activity.getType() != null && "Phone Call".equals(activity.getType().getName()));
        
        System.err.println(typeExists);

All the magic happens in that anyMatch() method that follows stream().

The anyMatch() method, incidentally, takes a Predicate as its only parameter. If you're unfamiliar with predicates, you can check out my explainer on predicates and functional interfaces.

In this case, the Predicate evaluates the activities in the collection. If any of them match the criteria, then the anyMatch() method returns true.

And that's something you should keep in mind, by the way. The anyMatch() method only returns a boolean. It doesn't return another list or a complex object. Just a boolean.

It's also worth noting that only one (1) object in the collection needs to match the criteria for anyMatch() to return true. 

If you want to get a boolean that returns true only if all objects in the collection match a specific criteria then take a look at allMatch().

Now check out the boolean expression inside anyMatch() in the code above. It's got two parts:

  • A check to ensure that activity type isn't null
  • A check to see if the activity type name equals "Phone Call"

If both of those expressions evaluate to true for any single object in the collection, anyMatch() returns true.

So if you run that code with the dataset that I linked to above, you'll see that it prints true in bright red letters in your console.

Because there are quite a few "Phone Call" types in that collection.

Another Check

Let's say you also just added the "Not Interested" outcome for activities. Once again, you've told the sales reps they can use it and once again they told you that yes, it works as designed.

You still need to be sure.

So you write some sanity-check code to ensure that the "Not Interested" outcome is getting persisted as expected.

What do you think that code would look like?

Maybe something like this:

        List<Activity> activities = activityRepo.findAll();
        
        boolean outcomeExists = activities
                                .stream()
                                .anyMatch(activity -> activity.getOutcome() != null && "Not Interested".equals(activity.getOutcome().getName()));
        
        System.err.println(outcomeExists);

And once again, that prints true.

If you check out the dataset, you'll find that there's one and only one object with a "Not Interested" outcome. But that's all that's needed for anyMatch() to return true.

So that test is a success.

Wrapping It Up

Now it's your turn. How do you want to use anyMatch() to accomplish your software development objectives?

Man, did that sound like an interview question or what?

Anyway, you've got a better understand of how to use a Stream to search for a single match with criteria. It's a powerful feature that you'll likely combine with other Stream operations.

And then you'll deliver outstanding applications.

Have fun!

Photo by Karol D from Pexels