Thursday, September 16, 2021

How to Filter Stream and Collections in Java 8? Example Tutorial

Java 8 provides excellent features to support the filtering of elements in Java Collections. Prior to Java 8, the only better way to filter elements is by using a foreach loop or iterating over Collection using the Iterator and selecting the required object, leaving out rest. Though that approach work, it was very difficult to run them in parallel and take advantage of multiple CPUs available in modern-day servers. Java 8 provides Streams, which not only makes it easy to run any operation parallel but also supports lazy loading and lazy evaluation, which means as soon as the filtering condition is satisfied, it stooped doing work, doesn't matter how many objects the collection contains.

You can filter Java Collections like List, Set or Map in Java 8 by using the filter() method of the Stream class. You first need to obtain a stream from Collection by calling stream() method and then you can use the filter() method, which takes a Predicate as the only argument.

Predicate is a functional interface with a couple of boolean-valued methods e.g. test(), which returns boolean true or false. Java 8 uses this method to filter Collection. Just remember that filter doesn't remove elements that match the condition given in the predicate, instead, it selects them in the output stream.

I agree a little bit counter-intuitive, select would have been better named for this method, but once you used it a couple of times, you will be Ok. For example, if you have a list of String and you want another list that contains only a long string say whose length is greater than 20 characters, you can use a filter method to do that and you don't need a loop.

The  Java 8 Stream is a very efficient replacement of looping both design and performance-wise, because it separates What to do from how to do, just like SQL. Leaving the implementation part of the platform.

And, If you are not familiar with Lambda Expression and Stream in Java then  I suggest you check to Learn Java Functional Programming with Lambdas & Streams by Rang Rao Karnam on Udemy, which explains both Functional Programming and Java Stream fundamentals in good detail




Java 8 Filter Method

As I said filter() method is defined in Stream class, it takes a Predicate object and returns a stream consisting of the elements from the original stream which matches the given Predicate. It is an intermediate operation, which means you can call any other method of Stream like count() as the stream will not be closed due to filtering.

The Predicate is applied to each element of the Collection to check whether a particular element should be included in a filtered stream or not. The predicate should be stateless and non-interfering so that if needed filter operation can run in parallel using parallel stream. 

In Java8, Predicate is a functional interface with a couple of boolean-valued methods used to test input against the condition.

In our case test(T t) method is used, which evaluates this predicate in a given argument. Since the filter() method accepts a functional interface, you can also pass a lambda expression to it, which is what we will do. 

You can pass any condition to filter elements either by using relational operators like less than, greater than, or equal to, or by using methods like equals() and equalsIgnoreCase() as shown in our sample program. 



Java 8 Example of Filtering List using Stream

This is our sample program to demonstrate the power of the Java 8 Stream and Filter method. By using these Java 8 enhancements you can perform SQL like operations in Java-like:

SELECT * FROM Deals WHERE type = 'ELECTRONIC'

can be written using Java 8 stream and filter method as

 deals.stream()
        .filter(deal -> deal.type() == Deal.Type.ELECTRONIC)
        .forEach(System.out::println)

In this example, we are passing a lambda expression to the filter method, which returns a boolean. It's actually an anonymous function test(), we have omitted type information for the deal variable because it will be inferred by JVM easily, making our code concise.

In short, you can pass a lambda expression to the filter method until the result is boolean. The forEach() method is a terminal operation and is used here to print all deals present in the filtered collection.

Now let's see the second example of a filtering List in Java. Now we need all deals which are expiring in March. In SQL we can write a query like this :

SELECT * FROM Deals WHERE validity='MARCH'

and in Java 8, we can write the following code :

deals.stream()
         .filter(deal -> deal.validity().getMonth() == Month.MARCH)
         .forEach(System.out::println);

Our third example is about filtering elements based upon greater than condition. How about getting all deals which with 30% or more discounts? We can write the following query in SQL :

SELECT * FROM Deals WHERE discountPercentage >= 30;

In Java 8 you can use a filter method  like the below to do the same :

deals.stream()
        .filter(deal -> deal.discount()
                               .compareTo(new BigDecimal("00.30")) > 0)
                               .forEach(System.out::println);


Let's see one more example of filtering collection in Java. How about finding all deals from Apple, don't you know iPad and iPhone? We can write SQL queries like the following to get those deals :

SELECT * FROM Deals WHERE provider='Apple';

In Java 8, You can do  :

deals.stream()
        .filter(deal -> deal.provider().equalsIgnoreCase("Apple"))
        .forEach(System.out::println);

Here is the complete code which you can run in your favorite IDE or command prompt if you are a down-to-earth programmer.




How to use the Stream. filter() method in Java 8

Here is our complete Java code example to demonstrate how to use the filter() method of Stream class to filter objects from Stream in Java:
package test;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;

/**
 * Simple Java value class to represent a deal
 */ 
public class Deal {

    public enum Type {
        BOOK,
        ELECTRONIC,
        TRAVEL,
        COSMATIC,
        ACTIVITY,
    }
    private final String provider;
    private final Type type;
    private final BigDecimal price;
    private final BigDecimal discount;
    private final String title;
    private final LocalDate validity;

    public Deal(String provider, Type type, BigDecimal price,
            BigDecimal discount, String title, LocalDate validity) {
        this.provider = provider;
        this.type = type;
        this.price = price;
        this.discount = discount;
        this.title = title;
        this.validity = validity;
    }

    public String provider() {
        return provider;
    }

    public Type type() {
        return type;
    }

    public BigDecimal price() {
        return price;
    }

    public BigDecimal discount() {
        return discount;
    }

    public String title() {
        return title;
    }

    public LocalDate validity() {
        return validity;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(title).append(" from ").append(provider).
                append(", price : ").append(price).
                append(", offer valid till ").append(validity).
                append(" category : ").append(type);
        return sb.toString();
    }

}


/**
 * Java 8 Example to filter Collection on Predicates using Stream API. this
 * program also uses new Date and Time API, lambdas, method reference etc.
 * 
* @author Javin Paul
 */
public class Java8FilterDemo {

    public static void main(String args[]) {

        List deals = loadDeals();
        System.out.println("All Deals");
        System.out.println("--------------------------------");

        // this will print all deals from list
        deals.forEach(System.out::println);

        System.out.println("--------------------------------");

        // Filtering elements from a Collection in Java 8
        // filtering on category
        System.out.println("All deals for Electornic items");
        deals.stream()
             .filter(deal -> deal.type() == Deal.Type.ELECTRONIC)
             .forEach(System.out::println);
        System.out.println("--------------------------------");

        // filter all deals which are expiring on March
        System.out.println("Deals expiring on March");
        deals.stream()
             .filter(deal -> deal.validity().getMonth() == Month.MARCH)
             .forEach(System.out::println);
        System.out.println("--------------------------------");

        // filter all deals which has more than 30% discount
        System.out.println("All deals with 30% or more discount");
        deals.stream()
             .filter(deal -> deal.discount()
             .compareTo(new BigDecimal("00.30")) > 0)
             .forEach(System.out::println);
        System.out.println("--------------------------------");

        // filter all deals from companies
        System.out.println("All deals from Apple");
        deals.stream()
             .filter(deal -> deal.provider().equalsIgnoreCase("Apple"))
             .forEach(System.out::println);
        System.out.println("--------------------------------");
    }

    private static List loadDeals() {
        List deals = new ArrayList<>();
        deals.add(new Deal("Manning", Deal.Type.BOOK,
                new BigDecimal("30.00"), new BigDecimal(".50"),
                "Save 50% on Java 8 Books", 
                 LocalDate.of(2014, Month.MARCH, 20)));

        deals.add(new Deal("Amazon", Deal.Type.BOOK,
                new BigDecimal("20.00"), new BigDecimal(".20"),
                "Save 20% on Clean Code", 
                 LocalDate.of(2014, Month.FEBRUARY, 10)));

        deals.add(new Deal("Kathy Pacific", Deal.Type.TRAVEL,
                new BigDecimal("300.00"), new BigDecimal(".40"),
                "Save 40% on flight to USA", 
                 LocalDate.of(2014, Month.FEBRUARY, 19)));

        deals.add(new Deal("Luftanse", Deal.Type.TRAVEL,
                new BigDecimal("30.00"), new BigDecimal(".50"),
                "Save 50% on flight to Berlin", 
                LocalDate.of(2014, Month.MARCH, 27)));

        deals.add(new Deal("Trekking", Deal.Type.ACTIVITY,
                new BigDecimal("400.00"), new BigDecimal(".50"),
                "Save 50% on Trekking", 
                LocalDate.of(2014, Month.MARCH, 25)));

        deals.add(new Deal("Apple", Deal.Type.ELECTRONIC,
                new BigDecimal("800.00"), new BigDecimal(".10"),
                "10% discount on iPhone 5S", 
                 LocalDate.of(2014, Month.APRIL, 19)));

        deals.add(new Deal("Samsung", Deal.Type.ELECTRONIC,
                new BigDecimal("700.00"), new BigDecimal(".20"),
                "20% discount on Galaxy S4", 
                 LocalDate.of(2014, Month.MARCH, 18)));

        deals.add(new Deal("LG", Deal.Type.ELECTRONIC,
                new BigDecimal("390.00"), new BigDecimal(".50"),
                "Save 40% on LG Smartphones", 
                 LocalDate.of(2014, Month.FEBRUARY, 17)));

        deals.add(new Deal("Sony", Deal.Type.ELECTRONIC,
                new BigDecimal("500.00"), new BigDecimal(".50"),
                "Save 50% on Sony Viao Laptops",
                 LocalDate.of(2014, Month.APRIL, 10)));
        return deals;
    }

}


Output:

All Deals
--------------------------------
Save 50% on Java 8 Books from Manning, price : 30.00, 
offer valid till 2014-03-20 category : BOOK
Save 20% on Clean Code from Amazon, price : 20.00, 
offer valid till 2014-02-10 category : BOOK
Save 40% on flight to USA from Kathy Pacific, price : 300.00, 
offer valid till 2014-02-19 category : TRAVEL
Save 50% on flight to Berlin from Luftanse, price : 30.00, 
offer valid till 2014-03-27 category : TRAVEL
Save 50% on Trekking from Trekking, price : 400.00, 
offer valid till 2014-03-25 category : ACTIVITY
10% discount on iPhone 5S from Apple, price : 800.00, 
offer valid till 2014-04-19 category : ELECTRONIC
20% discount on Galaxy S4 from Samsung, price : 700.00, 
offer valid till 2014-03-18 category : ELECTRONIC
Save 40% on LG Smartphones from LG, price : 390.00, 
offer valid till 2014-02-17 category : ELECTRONIC
Save 50% on Sony Viao Laptops from Sony, price : 500.00, 
offer valid till 2014-04-10 category : ELECTRONIC
--------------------------------
All deals for Electornic items
10% discount on iPhone 5S from Apple, price : 800.00, 
offer valid till 2014-04-19 category : ELECTRONIC
20% discount on Galaxy S4 from Samsung, price : 700.00, 
offer valid till 2014-03-18 category : ELECTRONIC
Save 40% on LG Smartphones from LG, price : 390.00,
 offer valid till 2014-02-17 category : ELECTRONIC
Save 50% on Sony Viao Laptops from Sony, price : 500.00, 
offer valid till 2014-04-10 category : ELECTRONIC
--------------------------------
Deals expiring on March
Save 50% on Java 8 Books from Manning, price : 30.00, 
offer valid till 2014-03-20 category : BOOK
Save 50% on flight to Berlin from Luftanse, price : 30.00, 
offer valid till 2014-03-27 category : TRAVEL
Save 50% on Trekking from Trekking, price : 400.00,
 offer valid till 2014-03-25 category : ACTIVITY
20% discount on Galaxy S4 from Samsung, price : 700.00, 
offer valid till 2014-03-18 category : ELECTRONIC
--------------------------------
All deals with 30% or more discount
Save 50% on Java 8 Books from Manning, price : 30.00, 
offer valid till 2014-03-20 category : BOOK
Save 40% on flight to USA from Kathy Pacific, price : 300.00, 
offer valid till 2014-02-19 category : TRAVEL
Save 50% on flight to Berlin from Luftanse, price : 30.00, 
offer valid till 2014-03-27 category : TRAVEL
Save 50% on Trekking from Trekking, price : 400.00, 
offer valid till 2014-03-25 category : ACTIVITY
Save 40% on LG Smartphones from LG, price : 390.00, 
offer valid till 2014-02-17 category : ELECTRONIC
Save 50% on Sony Viao Laptops from Sony, price : 500.00, 
offer valid till 2014-04-10 category : ELECTRONIC
--------------------------------
All deals from Apple
10% discount on iPhone 5S from Apple, price : 800.00, 
offer valid till 2014-04-19 category : ELECTRONIC

That's all about how to filter collection in Java 8.  This is just an example of the superpower of the new stream API. You can write more expressive code, which is easy to understand, easy to optimize, and super easy to run in parallel without you worrying about a multi-threading nightmare. Together with lambda expression, method references, and new Stream API, Java has become a super-expressive language without added boilerplate.


 If you like this Java 8 tutorial and are hungry for more Java 8 articles, don't forget to check out these :
  • How to use Lambda Expression in Place of Anonymous class (read here)
  • How to use Map function in Java 8 (see more)
  • How to use the Default method in Java 8. (see here)
  • Java 8 Comparator Example (see example)
  • Free Java 8 tutorials and Books (read the book)
  • Top 10 tutorials to Learn Java 8 (read here)
  • How to convert the stream to array in Java 8 (tutorial)
  • Java 8 Certification FAQ (guide)
  • Java 8 Mock Exams and Practice Test (test)
  • 5 New Java Features Every Java Developer should Lear (Java features)
  • 21 Tech Skills Java Programmers should learn (Java skills)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions, doubts, or feedback then please drop a comment and I'll try to answer your question.

P.S. : If you want to learn more about new features in Java 8 then please see these best Java 8 online courses. It explains all important features of Java 8  like lambda expressions, streams, functional interface, Optionals, new date and time API, and other miscellaneous changes.

2 comments :

Unknown said...

Great written!
And I have a question. How can you do with multiple conditions? Thank you.

javin paul said...

@Ng, You can chain filter() methods because its not a terminal method like forEach(). just add multiple filter() methods for multiple conditions.

Post a Comment