Thursday, May 18, 2017

Refactoring Java code using lambdas - Stream.filter

Refactoring Java Code Using Lambdas

I’ve been going through a book, and absolutely loving it - Java 8 in Action: Lambdas, Streams, and functional-style programming.

The thing that I’ve enjoyed the most so far are the notes on when to use certain methods. In the chapter on streams, chapter 3, there is one particular method that really stuck out for me - the filter method. 

The filter method takes a predicate, and returns a stream of all the elements that match the predicate.

The book points out the following:

“Any time you’re looping over some data and checking each element, you might want to think about using the new filter method on Stream.”

Here is an example - the following code will print out even numbers:

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

for (int num : numbers) {
    if (num % 2 == 0) {
       System.out.println(num);
    }
 }

The code above is pretty straight forward, but it could be written like this instead:

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

numbers.stream()
   .filter(n -> n % 2 == 0)
   .forEach(System.out::println);

This might not look like a huge advantage, because there isn’t a lot different between the two. The amount of code is basically the same as well.  However, the benefit of the lambda version is that it can be chained together with other Stream methods.

For example, imagine that you have some data containing user IDs, and some user IDs have a special prefix to indicate a special user type.  You might want to filter out the special users, and then return a list of users without the prefix and with the name in upper case letters.

List<String> userIds = Arrays.asList(“*alice”,”bob”,”*", "charlie","*dana","evelyn","*frank");

return userIds.stream()
   .filter(u -> u.startsWith(“*”) && u.length() > 1)
   .map(u -> u.substring(1).toUpperCase())
   .collect(Collectors.toList());

To do the same thing without using streams would look something like this:

List<String> userIds = Arrays.asList("*alice","bob","", "*", "charlie","*dana","evelyn","*frank");

List<String> specialUsers = new ArrayList<>();

for (String user : userIds) {
   if (user.startsWith("*") && user.length() > 1) {
      specialUsers.add(user.substring(1).toUpperCase());
   }
}

return specialUsers;

You can see that it would require that another variable would have to be declared to hold the special users. Kind of a waste. 


I’ll definitely be keeping my eyes open for code that is iterating over lists and inspecting each element!