In this post we’ll explore the Java stream filter. We will look at the Stream.filter()
method and see the different java stream filter examples, including the lambda expression.
Introduction:
Stream.filter()
method can filter out the stream elements based on a predicate. It takes one predicate as the argument and returns one new stream. Actually, it creates a new stream using the provided predicate. In this tutorial, we will learn how to use stream filter and how to handle an exception in the filter with different examples.
1. Simple stream filter example:
This is a basic example to give you an idea of how it works. In this example, we will create one list of orders. Each order will have three different properties: name, order type and the amount paid. We will learn how to filter out orders from the list based on any of these properties. Basically, we will create one list of filtered orders using stream filter.
Example Program:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
enum OrderType {
ONLINE,
OFFLINE
}
class Order {
private String customerName;
private OrderType orderType;
private float amount;
Order(OrderType _orderType, float _amount, String _customerName) {
this.orderType = _orderType;
this.amount = _amount;
this.customerName = _customerName;
}
public OrderType getOrderType() {
return orderType;
}
public float getAmount() {
return amount;
}
public String getCustomerName() {
return customerName;
}
static void printOrders(List < Order > orderList) {
orderList.forEach(e - > {
System.out.println("Customer Name : " + e.getCustomerName() + ", Order type : " + e .getOrderType() + " , Amount : " + e.getAmount());
});
}
}
public class Example {
public static void main(String[] args) {
Order[] orderArray = {
new Order(OrderType.OFFLINE, 1000, "Bob"),
new Order(OrderType.ONLINE, 500, "Rose"),
new Order(OrderType.OFFLINE, 400, "Nancy"),
new Order(OrderType.ONLINE, 1500, "Kate")
};
List < Order > orderList = Arrays.asList(orderArray);
System.out.println("Given Orders : ");
Order.printOrders(orderList);
List < Order > offlineList = orderList.stream()
.filter(item - > item.getOrderType().equals(OrderType.OFFLINE))
.collect(Collectors.toList());
System.out.println("Offline order list : ");
Order.printOrders(offlineList);
List < Order > belowThousandList = orderList.stream()
.filter(item - > item.getAmount() < 1000)
.collect(Collectors.toList());
System.out.println("Orders with less than 1000 : ");
Order.printOrders(belowThousandList);
}
}
1.1. Explanation of the program:
- OrderType is an enum with two values:
ONLINE
andOFFLINE
- Order class holds three values: one string name of the customer, one floating-point variable amount, and one
OrderType
variable to hold the order type.- This class has one constructor that takes three variables as arguments and assigns them.
- It also has one
static
methodprintOrders
to print the details of an Order object. It takes one List of Order objects and prints the details (name, order type and amount) for each.
- In the Example class, we have created one array of Order objects
orderArray
in the beginning. Next, we converted the array to a list usingArrays.asList
method. - We are applying two different types of filter to
orderList
.One to filter out all orders with order typeOFFLINE
and the other one is to filter out all orders with order amount is less than 1000. - For the first one, i.e.
offlineList
, we converted the List to a stream first. As explained before, thefilter()
method can be used only with streams.List.stream()
method converts one list to a stream with the list-objects. We can then applystream()
on that. It takes one predicate.item -> item.getOrderType().equals(OrderType.OFFLINE)
is the predicate we are passing as the argument. Using it, we are filtering the stream to hold only objects withOrderType = OFFLINE
.
filter()
returns one stream. The finalcollect()
method does one mutable reduction operation on the result stream and returns the final list.- Similar to
offlineList
,belowThousandList
will hold only orders with an amount less than 1000.
Output:
Customer Name : Bob, Order type : OFFLINE , Amount : 1000.0
Customer Name : Rose, Order type : ONLINE , Amount : 500.0
Customer Name : Nancy, Order type : OFFLINE , Amount : 400.0
Customer Name : Kate, Order type : ONLINE , Amount : 1500.0
Offline order list :
Customer Name : Bob, Order type : OFFLINE , Amount : 1000.0
Customer Name : Nancy, Order type : OFFLINE , Amount : 400.0
Orders with less than 1000 :
Customer Name : Rose, Order type : ONLINE , Amount : 500.0
Customer Name : Nancy, Order type : OFFLINE , Amount : 400.0
2. Using filter() with map and collect:
In this example, we are creating one class Customer to hold the following customer information: name and country.In a real-world production application, classes like this should hold more information than only these two but for simplicity, I will use only name and country.We will write one application that will store Customer objects in a map and based on a country name, it will filter out and create one list using the filtered object.
Example Program:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Customer {
private String name;
private String country;
Customer(String _name, String _country) {
this.name = _name;
this.country = _country;
}
String getCountry() {
return country;
}
private String getName() {
return name;
}
static void printCustomerDetail(List < Customer > customerList) {
customerList.forEach(e - > System.out.println("Customer name : " + e.getName() + " Country : " + e.getCountry()));
}
}
public class Example {
public static void main(String[] args) {
Map < Integer, Customer > customerMap = new HashMap < > ();
customerMap.put(1, new Customer("Boris", "UK"));
customerMap.put(2, new Customer("William", "UK"));
customerMap.put(3, new Customer("Simon", "UK"));
customerMap.put(4, new Customer("Paul", "Zimbabwe"));
List < Customer > customerList = customerMap.entrySet().stream()
.filter(x - > x.getValue().getCountry()
.equals(country)).map(item - > item.getValue())
.collect(Collectors.toList());
Customer.printCustomerDetail(customerList);
}
}
2.1. Explanation of the above program:
Here, customerMap
is a HashMap
with key an integer and value a Customer object. We have inserted four different customer objects to it with keys 1, 2, 3 and 4
. The variable ‘country
‘ holds the country name that we need to filter out. The customerList is the final list of customers we created by filtering out map customerMap. This list holds only those objects with ‘country
‘ equals to the value saved in variable ‘country’.
Following are the steps used to get the result:
entrySet()
method returns one set view of the map.stream()
method on the Set returns the stream.- Now, we can apply
filter()
on this returned stream. The predicate gets the value from the map i.e. the Customer object and compares the ‘country’ with the given ‘country’ variable. - Once we complete the above step, we will get one filtered stream of ‘Map’. But we need the result as a List. So, by applying ‘
map()
‘ method, we converted it to a stream of ‘Customer’. - The final ‘
collect()
‘ is used to convert the stream to a list, i.e. a list of ‘Customer
‘ objects.
If you execute the above program, it will print the below output :
Customer name : Boris Country : UK
Customer name : William Country : UK
Customer name : Simon Country : UK
i.e. all customers with the country variable equal to ‘UK’.We can also simplify the above example by getting all the values of the map and then applying stream()
on these values like below:
List < Customer > customerList = customerMap
.values()
.stream()
.filter(customer - > customer.getCountry().equals(country))
.collect(Collectors.toList());
It will print the same output.
3. Using stream filter() with multiple conditions:
For the above examples, we have used only one condition. We can add ‘n’ number of conditions in the predicate we are passing to the filter()
method. Let’s consider the below example:
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
class Student {
private String name;
private int marks;
Student(String _name, int _marks) {
this.name = _name;
this.marks = _marks;
}
String getName() {
return name;
}
int getMarks() {
return marks;
}
static void printStudents(List < Student > studentList) {
studentList.forEach(e - > System.out.println("Student name : " + e.getName() + " Marks : " + e.getMarks()));
}
}
public class Example {
public static void main(String[] args) {
Student[] studentArray = {
new Student("Jacob", 80),
new Student("Amanda", 70),
new Student("Ashley", 85),
new Student("Alexis", 90)
};
List < Student > studentList = Arrays.asList(studentArray);
Predicate < Student > studentFilterPredicate = new Predicate < Student > () {
@Override
public boolean test(Student student) {
return student.getName().startsWith("A") && student.getMarks() > 80;
}
};
List < Student > studentAList = studentList.stream()
.filter(studentFilterPredicate)
.collect(Collectors.toList());
Student.printStudents(studentAList);
}
}
Here, we have one ‘Student’ class that can hold one string variable ‘name
‘ and one integer ‘marks
‘. The ‘printStudents
‘ method prints the details of a list of ‘Student’s like the above examples.’studentList
‘ is the list with pre-defined ‘Student’ objects. We are using filter() to find the students those have name starts with ‘A’ and marks is more than ’80’.
The main difference in this example to the above examples is that we are using one custom Predicate ‘studentFilterPredicate
‘. The overridden ‘test
‘ method returns true if the name of the Student starts with ‘A
‘ and marks is more than ‘80
‘. We pass this variable as an argument to the ‘filter()
‘ method.
It will print the below output :
Student name : Ashley Marks : 85 Student name : Alexis Marks : 90
All students with names starting with ‘A
‘ and marks over 80
.
4. Using stream filter with findAny, orElse
The filter()
method returns one Stream. So, we can apply different stream-methods on the result. For example, the below code snippet uses findAny()
and orElse()
:
Student student = studentList.stream()
.filter(studentFilterPredicate)
.findAny()
.orElse(null);
findAny
returns one optional value if it finds anything using the provided predicate. orElse
returns ‘null
‘ if nothing found, else it returns the object that is found. You can try it by changing the predicate with different conditions. Similarly, we can use any other stream methods like sorted, distinct, limit, skip
, etc. on the return value of filter()
. For example, the below code snippet finds all students with marks greater than 40 in ascending order:
List < Student > sortedList = studentList.stream()
.filter(s - > s.getMarks() > 40)
.sorted(Comparator.comparingInt(Student::getMarks))
.collect(Collectors.toList());
It will print the below output :
Student name : Amanda Marks : 70
Student name : Jacob Marks : 80
Student name : Ashley Marks : 85
Student name : Alexis Marks : 90
5. Exception handling with stream filter:
We can have one predicate that throws an exception. For example, let’s change the ‘Student
‘ class as like below:
class Student {
private String name;
private int marks = -1;
Student(String _name) {
this.name = _name;
}
Student(String _name, int _marks) {
this.name = _name;
this.marks = _marks;
}
String getName() {
return name;
}
int getMarks() {
return marks;
}
boolean isMoreThan40() throws Exception {
if (marks == -1) {
throw new Exception("Marks is not defined");
}
return marks > 40;
}
static void printStudents(List < Student > studentList) {
studentList.forEach(e - > System.out.println("Student name : " + e.getName() + " Marks : " + e.getMarks()));
}
}
Here, we have added one new constructor that takes only ‘name
‘ and one more method ‘isMoreThan40
‘ to check if the current Student has marks more than ‘40
‘ or not. This method can throw one exception if the ‘marks’ variable is not set i.e. if it is ‘-1
‘. Now, if we try to use this method to filter out a ‘Student
‘ list those with marks over 40
like below:
List < Student > sortedList = studentList.stream()
.filter(Student::isMoreThan40)
.collect(Collectors.toList());
It will show one compile-time error: 'Unhandled exception: java.lang.Exception'
.
To handle this problem, we can use one ‘try-catch
‘ block :
List < Student > sortedList = studentList.stream()
.filter(s - > {
try {
return s.isMoreThan40();
} catch (Exception e) {}
return false;
}) collect(Collectors.toList());
Wrapping the exception throwing method with a try-catch
block solves the problem. You can write your exception handling code inside the ‘catch
‘ block. We are returning ‘false’ at the end i.e. if the ‘try-catch
‘ block throws one exception, it will return ‘false
‘.
Summary:
In this post, we explored the Java stream filter. As we have seen, filter()
is a useful method of Java Stream. It provides a quick way to get the desired outputs from a stream. Even we can use different steam methods on it. It is an intermediate operation and returns a new stream so we can apply other operations. The source code for this article is available on the GitHub.