toggle menu Close Menu

Refactoring for Java 8 Streams

Image Description
Image courtesy of

As a Java Developer who is very familiar with the Java Collections API, I was excited to learn about the Lambdas and Streams APIs that are a part of the Java 8 release. An increase in performance when processing collections, and a lean coding syntax were both promised, and it appears the Java 8 team has delivered. This release provides a whole new way to process collections of data, along with new and more efficient coding patterns and syntax. In addition, the API takes advantage of parallel architectures right out of the box!

When you first learn about Lambdas, Streams, and the new syntax, you may think that the examples and tutorials found online are a bit trivial, but that’s actually a good thing. It can take some time for these concepts to sink in. They are all interrelated, and build upon new types and interfaces that were introduced in Java 8 in the java.util.function package. The concept of functional interfaces is crucial to understanding Lambdas, and Lambdas must be understood if you are to understand Streams. You have to delve a little bit into the background of functional interfaces and Lambdas to see why they make sense, and then you have to understand - and get used to – some brand-new notation for specifying Lambdas. From there, it’s another intellectual jump to actually use them in practice with the new methods that are provided for Streams processing.

As a professional developer, I am very much concerned with the best road for future development, especially considering the performance improvements that Java 8 brings. But with such a large change to the language around Collections processing, it is very clear there could be numerous opportunities for refactoring existing Collections code for greater efficiency. In this blog post, I intend to walk through the conversion of a small piece of Java 7 code into Java 8 code using Lambdas and Streams, both as a demonstration of some of the concepts, and to provide a starting point for your own investigation.

This is not a tutorial on functional interfaces, Lambdas, or Streams – there are plenty of resources online on those subjects (see the links at the end of this post for more information). This post is concerned with the thought process behind substituting the old way of iterating over a collection with the new Java 8 way. For the reader, I am less concerned with the best and most efficient notation for the code than I am with the concepts and understanding of the new methods, and how to use them in a simple refactoring example.

Code Comparisons

Let’s take a look at some example code that is written for the Java 7 Collections API. This code is designed to get a sorted list of Customer Profiles from a list of active Campaigns, by using a list of Participants in those Campaigns. Customer Profiles will be collected from each Campaign by iterating over the Campaign’s Participants, and returning all Participants with the desired campaign code of “1”.

Java 7

First, we have to populate our new list of Customer Profiles according to our requirements:

List customerProfiles = new ArrayList<>();
for (Campaign c : campaigns) {
    if (c.isActive()) { 
        for (Participant p : c.getParticipants()) {
            if (p.getCampaignCode() == 1) {

Next, we need the list of CustomerProfiles sorted on Name.

Collections.sort(customerProfiles, new Comparator() {
    public int compare(CustomerProfile c1, CustomerProfile c2) {
        return c1.getName().compareTo(c2.getName());

Note above that we are mutating the customerProfiles object during the sort.

Deep Dive into Java 8!

So now let’s use Java 8 methods and notation to solve this same coding problem.

The first jump we have to make is thinking of our input collection not just as a list of objects, but a list of objects that comes with a built-in iterator. Not only does it come with an iterator, but the iterator is already “switched-on”, and ready to make the elements available with one method call!

In Java 8, it is already assumed that you will be iterating over a list, so it’s made easy for you to do via the stream() method.

The statement is the new Java 8 way of iterating over a Collection and providing the objects for processing:

This method call – which can be called on any Collections object - opens up the Stream to start producing the objects in the list for you, one-by-one.

Notice that the for-loop is no longer needed! With Streams, we get that for-loop action automatically.

Working with the stream.

Since the stream is now actively returning objects to us, we had better do something with them.

We have a list of Campaigns, so now we have to look at the logic to see what other kinds of operations we will need to perform to get our result. Looking in our Java 8 toolbox, we see that it includes these items:

     filter() – select records based on some criteria

     map() – transform object into another object

Certainly, from looking at our old code, we know we have some selection going on. We only want to select active campaigns, and we want to select only Participants with Campaign Code of 1.

We know a filter() does this, so let’s use it to specify the selections. We are streaming the type of “Campaign”, so we’ll use a variable placeholder on the left of the Lambda notation arrow.

In addition, we are selecting only active Campaigns, so that selection criteria will go on the right, and return a boolean. The filter() method will, of course, filter the data based on the results of the boolean expression.

Using Lambda notation, this is fairly straightforward:

filter(campaign -> campaign.isActive())

Looking at the old code, we see we also have to iterate over, and filter, a list of type “Participant”. That’s also simple enough to write:

filter(participant -> participant.getCampaignCode() == 1))

But wait a second! We don’t have a stream of participants yet to filter. We are streaming Campaigns at the moment. How are we supposed to get into the Participant list?

Participant List

This is where map() comes in. You have to look at the map() method as a way to do something based on something that is given, and then return something else.

In the case of functional interfaces like map(), the behavior is intentionally defined in such a general and abstract way so that it can be applied to all kinds of problems.

In our case, we want to give the map() a single Campaign, and we want the map to return to us a list of Participants, based on that Campaign.

So, we are essentially transforming our stream of Campaign objects, one-by-one, into a different stream of Participant objects, one list for each Campaign:

map(campaign -> campaign.getParticipants())

So our built-up code now looks like this:
         .filter(campaign -> campaign.isActive())                                               
         .map(campaign -> campaign.getParticipants())
         .filter(participant -> participant.getCampaignCode() == 1))

Customer Profiles

Now we have to get the Customer Profile for each participant. Would that be a map or a filter? Since we are giving a Participant, and getting a Customer Profile for that Participant, then that would be
another map operation.

map(participant -> participant.getCustomerProfile())

Now we are streaming (and working with) Customer Profiles! We started at a Campaign, and have finally drilled down to the stream of Customer Profiles that we want. So, let’s sort them according to the Customer Profile name using the standard Lambda notation for the sorted method.

             customerProfile -> customerProfile.getName()))

Return List

The last step is to put them into a return list, from the output of our sorter. Remember, we are still using the stream methods, so we are dealing with objects ready to be processed. At the end, the stream must be put into a data structure for the next bit of processing.

A standard way of collecting results into a list is this statement:


Adding these things together, we get this as our first-cut refactored code.

List customerProfiles =        
       .filter(campaign -> campaign.isActive())
       .map(campaign -> campaign.getParticipants())
       .filter(participant -> participant.getCampaignCode() == 1)
       .map(participant -> participant.getCustomerProfile())
           customerProfile -> customerProfile.getName()))

Note that the Java8 coding is much more concise, and the selection and mapping operations are explicitly shown, and easy to understand. Also note that we have not mutated the original object, and we can easily plug in new processing at any point in the chain of Stream methods.

Next Steps

Although the code above is not the only way or even the “best” way to solve this problem using Java 8 facilities, it is a good starting point for seeing the concepts come together in a simple refactoring example.

I encourage the reader to the review how the Lambda statements shown above could be refactored yet again with a more shorthand notation, and also to read about and understand the various options for the collect() method.

I hope this example will help you to understand the first steps and thought processes behind refactoring some of your existing code for processing as a Stream in Java 8. As always, the key is to break down the problem into some discrete steps, keeping in mind the model and data structures. Once you get an idea of what kind of step you need to do – sorting, selecting, transforming, etc. – you can usually write a simple Lambda statement and pass it to a Stream method call to get the task done. Happy coding!


Further reading on functional interfaces:

For a good start on Lambdas:

For a tutorial on Streams:

Sean Sealy
Java Developer
or drop us a note and say hello!