Pages

Saturday, November 21, 2020

Java 8 Stream Filter with Lambda Expression + Predicate Filter Examples

1. Overview

In this article, You'll learn and explore the usage of different ways of the Stream.filter() method. 

Let us see the examples programs on how to use it effectively stream filter options and examples with checked exceptions in java. 8 with lambda.

Java 8 Stream Filter with Lambda Expression


2. Using Stream.filter() Method

The Stream API has a filter() method and it is an Intermediate Operations.

filter() syntax:

Stream<T> filter(Predicate<? super T> predicate)
 

This method runs the given predicate on all the values of stream objects and returns a filtered stream as a returned object.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {

    public static void main(String[] args) {

        List<Integer> numbers  =  Arrays.asList(10,50,30,18,35);

        List<Integer> evenNumbers  =  numbers.stream().filter(number ->  number % 2 ==  0).collect(Collectors.toList());

        System.out.println("After applying the filter function : "+evenNumbers);

    }
}
 

Output:

After applying the filter function : [10, 50, 30, 18]

 

3. Filter on Custom Objects

As of now, you have seen how to use filters with a list of string objects. But, now let us see how to use filters on the List of Employee objects.

Creating an Employee class with id, name, and age properties and setters, getters, toString() methods.

public class Employee {

    private int id;
    private String name;
    private int age;

    public Employee(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
	
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 

Create 5 employee objects and add them to a List using Arrays.asList() method.

		Employee e1 = new Employee(100, "Ram baba", 50);
		Employee e2 = new Employee(101, "Tricky joy", 25);
		Employee e3 = new Employee(102, "Johon perth", 45);
		Employee e4 = new Employee(103, "abran parande", 21);
		Employee e5 = new Employee(104, "naha ama", 59);

		List<Employee> emps = Arrays.asList(e1, e2, e3, e4, e5);
 

In the below example, you are going to filter the employees whose ages are greater than 35 using the filter() method.

List<Employee> emps = Arrays.asList(e1, e2, e3, e4, e5);

System.out.println("Emp list size  : " + emps.size());

List<Employee> filteredList = emps.stream().filter(emp -> emp.getAge() < 35).collect(Collectors.toList());

System.out.println("Emp list size after filter() : " + filteredList.size());
 

Output:

Emp list size  : 5
Emp list size after filter() : 2
 

4. Filtering with the collection with multiple criteria

Yes it is allowed to use the filter() method with multiple fields and also filter() method can be called multiple times.

// way 1
List<Employee> multipleCriteriaList = emps.stream()
		.filter(emp -> emp.getAge() > 40 && emp.getName().contains("a")).collect(Collectors.toList());
System.out.println("Multiple fields criteria: way 1 - " + multipleCriteriaList.size());

// way 2
List<Employee> multipleCriteriaList1 = emps.stream().filter(emp -> emp.getAge() > 40)
		.filter(emp -> emp.getName().contains("a")).collect(Collectors.toList());
System.out.println("Multiple fields criteria: way 2 - " + multipleCriteriaList1.size());
 

Both of these methods will produce the same output with size 2.

5. Handling Exceptions with Custom Classes or Checked Exceptions

There are chances that logic inside the filter() method can throw some checked or unchecked exceptions.

for example,  you are calling some method using method reference.

List<Book> validBooks = books.stream().filter(Book::isValidBookLocation).collect(Collectors.toList());

This isValidBookLocation() method throwing an IOException which causes the compile-time error.

Incompatible thrown types java.io.IOException in functional expression

or 

Unhandled exception type IOException

Full Example:

The below program throws the compile-time error

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.net.ssl.HttpsURLConnection;

public class FiterThrowsException {

	public static void main(String[] args) {

		Book b1 = new Book(123, "/bookstore/100/23/book123.pdf");
		Book b2 = new Book(145, "/bookstore/100/45/book145.pdf");
		Book b3 = new Book(167, "/bookstore/100/67/book167.pdf");

		List<Book> books = Arrays.asList(b1, b2, b3);

		List<Book> validBooks = books.stream().filter(Book::isValidBookLocation).collect(Collectors.toList());

	}

}

class Book {
	private long id;
	private String path;

	public Book(long id, String path) {
		this.id = id;
		this.path = path;
	}

	public boolean isValidBookLocation() throws IOException {
		URL url = new URL(this.path);
		HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
		return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
	}

}
 

To handle such exceptions either need to throw the same exception or wrap the code inside a try-catch block.

From lambda expressions, there is no way to throw the exceptions but you can use the try-catch block.

Use the below code to make it work above example.

List<Book> validBooks = books.stream().filter(t -> {
	try {
		return t.isValidBookLocation();
	} catch (IOException e) {
		System.out.println("Errorn while checking the file : "+e.getMessage());
		e.printStackTrace();
	}
	return false;
}).collect(Collectors.toList());
 

If you want to throw any exception from Predicate then wrap it as RuntimeException.

6. Exception Handling Using ThrowingFunction

Furthermore, There is another way to handle the exceptions from stream or lambda using an opensource library.

Add the following the dependency in the pom.xml file or add the jar to the project.

<dependency>
	<groupId>pl.touk</groupId>
	<artifactId>throwing-function</artifactId>
	<version>1.3</version>
</dependency>
 

To handle such unchecked exceptions, touk api provided with a class ThrowingFunction and which. has unchecked() method. to wrap the throwing exception inside the predicate function.

List validBooks = books.stream().filter(ThrowingFunction.unchecked(Book::isValidBookLocation))
 								.collect(Collectors.toList());
 

But this code does not work because the filter() method needs the Predicate but unchecked() returns Function type. So, this will not work here. I have seen in the other websites showing the examples like passing the output of the unchecked() method to the filter()  method. 

But, you can pass this to the Stream.map() method as below.

Stream.map() vs Stream.filter()

Stream<Boolean> validBooks = books.stream().map(ThrowingFunction.unchecked(Book::isValidBookLocation));
 

7. Conclusion

In this article, you've seen how to use the filter() method in context of lambda expression in java 8  and. how to handle the unchecked exceptions from predicate function.

Strem filter ref

ThrowingFunction

No comments:

Post a Comment

Please do not add any spam links in the comments section.