Stream Filter in Java 8 with Examples

Learn via video course
FREE
View all courses
Java Course - Mastering the Fundamentals
Java Course - Mastering the Fundamentals
by Tarun Luthra
1000
5
Start Learning
Java Course - Mastering the Fundamentals
Java Course - Mastering the Fundamentals
by Tarun Luthra
1000
5
Start Learning
Topics Covered

The Stream Filter in Java is a filtration method for streams of collections. We can modify the streams using the filter() method to get only the desired elements and store them in a new list or perform more operations. It takes a predicate function that helps filter elements as a parameter and returns another stream. Though it seems simple and interesting, it must be used carefully and with best practices.

What is a Stream Filter in Java?

Stream filter in Java 8 generates a stream of elements that satisfy the given condition in the predicate function. Stream is a sequence of elements typically present in a list data structure of Java.

This stream can be saved in a file, saved in another list, or simply presented to the user with filtered elements. In other words, it takes a series of elements, filters them according to the specific condition, and returns the series of elements that pass the filter test.

It is similar to filtering tea leaves from tea before drinking. We separate tea leaves using filtration paper, representing our filter() method. The tea, along with tea leaves, is the initial list, and filtered tea is the filtered stream of elements.

Syntax

The syntax of the Stream filter method is as follows:

Stream is a generic interface that contains the filter() method. This method takes a predicate and returns the stream of elements that match the given predicate.

Parameters

The filter() method of the Stream interface takes a predicate as an input.

Predicate: Predicate is a functional interface in Java 8 that can be used to pass a lambda function as an argument to the filter method. Lambda Expression Lambda functions are inline functions that can take arguments and return values.

The filter function must always return a boolean value, true or false. When it returns true, the element is sent to the output stream and discarded.

Also filter method is part of a generic interface indicating that it can work on all the data types such as integers, strings, etc., or class types and depends on the type of the list we are using.

Return Type

It returns a new stream element that is now filtered. The filter() function performs the lazy operation and, therefore, rather than filtering the existing list, creates a new list containing elements of the initial stream that matches the given predicate.

The predicate function must be written carefully as it may result in an exception. There is no exception handling in streams or inside the filter() function. We can write a predicate inside the try-catch block to avoid runtime errors.

Implementation of Stream Filter in Java

To implement the filter() operation with the help of Streams, we need to have a list of elements and some conditions that can be applied to the Predicate.

  • Filtering a list of elements to find the elements in a specific range and sorting them.
  • Filtering strings containing a specific pattern.
  • Finding an element that matches the target element. Here, we will also represent the case where the stream is empty and the filter() operation is short-circuited.

Example 1

In the example 1, we will take a random list of elements and find the elements present in the range 20 to 50. Next, we will sort this list using the .sort() function.

Output:

Explanation:
The above list consists of some random numbers. We stream the given list to filter the list and find the elements in the given range. Once the elements are filtered, they are sorted in ascending order. We know that stream is returned without affecting the original list so we will store this result in another list of the same Integer data type.

Example 2

Let us take another example of a list of strings and find the strings that contain some pattern in it using the filter() method. The Stream Filter in Java will check each element individually to match the given pattern and return the stream of all the matching elements.

Output:

Explanation:
We initially have a list of fruits with us. The filter operation is to find the pattern ap in the names of the fruits. This can be facilitated by the contains() method of the String class. It returns true if the pattern exists else false. Lastly, we use the .collect(Collectors.toList()) method to collect all the elements of the stream and store and convert them into a list.

Example 3

Lastly, let us encounter short-circuiting in the filter() function and see what happens when the stream() does not return a result.

Short-circuiting means ending the filtration when the stream has already returned a certain result. This is useful in optimization and avoiding useless operations.

We will use the same fruit list we used above.

Output:

In the first case, we are just looking for a single occurrence of the target1 and when we find it we simply exit the stream and all the other operations.

The second target doesn't exist in the list. After the stream completes the list traversal, nothing is returned. It doesn't throw an error or exception; it simply initializes an empty list.

Performance Considerations and Best Practices

Some of the best practices to be considered while using Stream Filter in Java are:

  • Minimize the List: Filter the list to minimize it as soon as possible in your performing series of operations. This is because if the operation is, say, sorting. It makes sense to first filter the list and then sort as sorting takes longer and thus must be performed on a shorter list.
  • Memory Usage Must be Optimized: We can either limit the number of elements in the resulting stream or use a function like collect(Collectors.toList()) to convert the filtered data into a list.
  • Use Parallel Streams for Large Datasets: For many elements in the list, use the function parallelStream() instead of only stream(). This can make the operations faster and, thus, improve performance in case of CPU-bound tasks. This may not be suitable for I/O-bound operations. Best practices include using stateless and threadsafe predicates in filter() operations to ensure correctness in parallel processing.
  • Use Efficient Predicates: The predicate function that we are implementing must be efficient so that we can quickly evaluate whether an element should be included or excluded. This function should also not have any side effects; in general, it should not generate any undesired results.
  • Use Primitive Data Types Instead of Objects: When working with numeric data, Reduce the overhead of autoboxing and unboxing the data. Use primitive data types such as int, float, and double instead of Integer, Float, Double, etc., and specialized predicates like IntPredicate and DoublePredicate.
  • Short-circuit Terminal Operation: Use operations such as findFirst(), anyMatch(), and noneMatch() to stop processing elements as soon as a certain condition is satisfied. This will optimize the function and reduce the number of expensive operations.
  • Avoid Intermediate Operations: Intermediate operations include map() or similar functions. The filter is a lazy function, and this creates an entirely new list, which can be expensive if the stream is large.

Conclusion

  • The filter() method is a Stream Operation in Java 8 that lets us create a stream of selected elements from existing elements based on the specified condition.
  • The filter method passes each element through the condition present in the predicate. The function passed in the predicate must return true or false in case of a filter operation. If it returns true, then the element is selected; otherwise, it is discarded.
  • The filter method can be called on a stream. It takes a predicate as an input and returns a stream.
  • Rather than affecting the original stream,, it generates and returns a new stream.
  • The filter method comes from the generic interface Stream, which implies that it can be applied to all the data types and classes.
  • Though simple, it may lead to heavy memory usage and large time complexities. This method must be optimized and efficiently used.

FAQs

Q. What happens if the predicate in the filter() returns false for all elements in the Stream?

A. In this case, an empty Stream is returned.

Q. Is the original Stream modified when filter() is applied?

A. No, the filter() method returns a new Stream, and the original Stream remains unchanged.

Q. What happens if I pass a null predicate to filter()?

A. If you pass a null predicate, a NullPointerException will be thrown.

Q. What types of elements can I filter using filter()?

A. You can filter elements of any type as long as you provide an appropriate predicate.