Preparing for Java Interview?

My books Grokking the Java Interview and Grokking the Spring Boot Interview can help

Download PDF

Java 8 Stream.filter() example Example with Null and Empty String

The filter() is a method on Stream class, which accepts a Predicate (a functional interface with a method which return boolean) and returns a stream consisting of the elements of this stream that match the given predicate. For example, if stream is obtained from a list containing "SONY", "APPLE" and "GOOGLE" and predicate is elements with length greater than 4, then filter will return another stream containing only APPLE and GOOGLE, leaving SONY out because its length is not greater than 4. Java 8 provides advanced filtering capability using lambdas and predicates. 

You can filter elements from list, map and set by providing clear and concise lambda expression as Predicate functional interface e.g. to filter all persons whose name starts with "J" you can use list.filter(). This will create another list with persons whose name starts with letter J.

While using filter method on Java, one of the main problem I faced is whether is filter in or filter out. I mean why I apply filter method I get a Stream back but many people including me get confused whether it contain elements which pass the filter or fail the filter.

To solve that problem, I think and remember  filter() as allow() method which means any element which pass the condition will be available after filter. There may be a better way to remember how filter works in Java but this method is working for me so far.

And, now it's time to go back to filter method and learn how you can use it to filter null, empty string, and elements with any condition you want. 

How to filter all null String from a List?

Here is an example of filtering out null elements from a List using Stream.filter() method in Java:

List<String> techGiants = new ArrayList<>(Arrays.asList("SONY",
                                    "GOOGLE", "APPLE", null, ""));
 techGiants.stream()

                .filter(Objects :: nonNull)

                .forEach(System.out::println); // SONY, GOOGLE, APPLE, ""


The List contained "SONY", "GOOGLE", "APPLE", null, "" but after filtering it only contains SONY, GOOGLE, APPLE, "". In this example though I have only printed the value from Stream so there won't be any impact on original List but you can also collect the result in another List and assigned to the same reference variable like shown below:

List<String> withoutNulls =  techGiants.stream()

                                       .filter(Objects :: nonNull)

				       .collect(Collectors.toList());



How to filter all null and empty String from List?

We can use the same pipeline as shown above and then add another condition to filter empty String as shown below:
List<String> techGiants = new ArrayList<>(Arrays.asList("SONY",
                                    "GOOGLE", "APPLE", null, ""));
techGiants.stream()
                .filter(Objects :: nonNull)
                .filter( s->> !s.isEmpty())
                .forEach(System.out::println); 

This will only print, SONY, GOOGLE, and APPLE as both null and empty String are filtered out. Though, you can also combine both condition at same filter method as well, which I think would be slightly more efficient as shown below:
List<String> techGiants = new ArrayList<>(Arrays.asList("SONY",
                                    "GOOGLE", "APPLE", null, ""));
List<String> withoutNullsAndEmptyString
 =  techGiants.stream()
   .filter(Objects :: nonNull && String :: isEmpty())
                      .collect(Collectors.toList());


You can write the code you feel better but when you use Stream pipeline, I think keeping condition simple improves readability. 



How to chaining of filter by combining multiple predicates

You can even combine multiple predicates to do more advanced filtering e.g. to filter all String which are null or empty you can do something like shown in above example but you are free with conditions, you can use whatever condition you want. 

For example, you can also filter based upon String as shown below:


ist<String> withoutNullsAndStringWithFiveOrMoreCharacters = 

       techGiants.stream()

                .filter(Objects :: nonNull)

                 .filter(s->> s.length() >= 5)

                 .forEach(System.out::println); // GOOGLE, APPLE


You can see that here we have combined two filter() methods, you can combine as many as you want, you can even combine filter with map() and flatMap method but make sure your pipeline is not very complex and read nice otherwise it would be very difficult for any person to read and understand what is going on.

Also, here is a nice diagram of creating Stream pipeline in Java and how it works:

Java 8 filter example Example with Null and Empty String



Java Program to use filter() method in Java 8

Here is a complete Java program which demonstrate the use of filter method in Java 8.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;


/**
 * Java Program to show how to use filter() method of Java 8 Stream.
 * By using filter() method you can remove unwanted elements from
 * original stream or list. filter() accepts a boolean method, only elements
 * for which this method return true are included into result. 
 *
 * @author WINDOWS 10
 */
public class Java8Demo {

    public static void main(String args[]) {

        List<String> techGiants = new ArrayList<>(Arrays.asList("SONY",
                                    "GOOGLE", "APPLE", null, ""));
        
        // let's filter out null elements and print rest
        techGiants.stream()
                .filter(Objects :: nonNull)
                .forEach(System.out::println); // SONY, GOOGLE, APPLE, ""
        
        
        // let's filter out both empty and null String
        techGiants.stream()
                .filter(Objects :: nonNull)
                .filter( s->> !s.isEmpty())
                .forEach(System.out::println); // SONY, GOOGLE, APPLE
        
        
        // let's filter out String whose length is less than 5
         techGiants.stream()
                .filter(Objects :: nonNull)
                 .filter(s->> s.length() >= 5)
                 .forEach(System.out::println); // GOOGLE, APPLE
                 
       
    }

}


Important things to remember 


1) filter is evaluated lazily i.e. when a terminal operation is called e.g. forEach(), reduce() etc.

2) filter method is defined in java.util.stream.Stream() class

3) You can also call filter with parallel stream


Another thing I noticed is that a lot of people used filter instead of if while writing functional style of code which I don't think a very good idea as it's not that intuitive to read because we are so used to reading condition using if block but I am open to hear what is your suggestion? Do you like using filter in place of if? I will show you the example at the end of this article so make sure you read till the end.

Let's consider a simple example where we have a method which return a Socket connection if its connected or return a new SocketConnection, I will write code using both if condition and filter method and you let me know which method you find it better while reading code? Let me know in comments and if you can mention why, would be even better.

So here is the first method using filter() and written in functional style in Java

 private static Socket getActiveOrNewSocket(Supplier<Socket> socketSupplier) {
        return Optional.ofNullable(socketSupplier.get())
                .filter(Socket::isConnected)
                .orElseGet(() -> createNewSocket("localhost", PORT));
    }

In this case, not just filter() but also Optional is there which many Java developer may not find so easy to read but you can achieve the same functionality using traditional if statements instead of Optional. 


Here's the method rewritten using if blocks:

private static Socket getActiveOrNewSocket(Supplier<Socket> socketSupplier) {
    Socket socket = socketSupplier.get();
    
    if (socket != null && socket.isConnected()) {
        return socket;
    } else {
        return createNewSocket("localhost", PORT);
    }
}

Now, let me know which method you find it easier to read in comments? 

That's all about how to filter elements form list and map in Java using Stream.filter() method. It's very efficient because its evaluated lazily. You can even chain multiple filters for more advanced filtering. I recommend every Java developer to master this method as it also help you write functional style of code because you will find filter almost in every single Stream pipeline. 

Other Java 8  Lambda and Stream Tutorials You may like


Thanks for reading this tutorial so far. If you like this Java 8 Stream and filter method tutorial then please share it with your friends and colleagues. If you have any questions or feedback then please drop a note.

No comments:

Post a Comment

Feel free to comment, ask questions if you have any doubt.