How to Filter a Stream of Generic Types in Java?

How to Filter a Stream of Generic Types in Java? A Comprehensive Guide with Code Examples

Java’s Stream API provides a powerful tool for processing sequences of elements in a functional programming style. When working with generic types, filtering a stream becomes an essential operation to refine and manipulate data effectively. This guide delves into how to filter a stream of generic types in Java, offering insights, explanations, and practical code examples that can be leveraged in various programming scenarios.

What Are Streams in Java?

Introduced in Java 8, the Stream API is designed to process sequences of data in a declarative manner, typically allowing operations like filtering, mapping, and reducing. Streams abstract the idea of a collection of data, enabling developers to write concise and expressive code for processing that data. Filtering is one of the most commonly used operations when working with streams.

Working with Generic Types in Java

Generics in Java enable developers to write classes, interfaces, and methods that work with any type of data while maintaining compile-time type safety. When it comes to filtering a stream of generic types, understanding the concept of generics and how they can be leveraged in conjunction with streams is crucial.

A generic type is a type that can operate on objects of various types while providing compile-time type safety. This means that you can write more reusable and flexible code without compromising on type safety.

Note: Generics in Java are used extensively to work with collections, ensuring that the elements being processed are of the correct type.

How to Filter a Stream of Generic Types?

Filtering a stream of generic types involves using the filter() method from the Stream API. The filter() method takes a predicate (a condition) and returns a new stream consisting of elements that satisfy the given condition.

When working with generic types, the type of the elements in the stream is not fixed. Hence, you can filter the stream based on any property of the objects in the stream.

Generic Filter Method Example

Here’s a simple example of filtering a stream of generic types:

    import java.util.*;
    import java.util.stream.*;

    public class GenericStreamFilter {
        // Method to filter the stream based on a condition
        public static  List filterStream(List list, Predicate condition) {
            return list.stream()
                    .filter(condition)
                    .collect(Collectors.toList());
        }

        public static void main(String[] args) {
            List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            // Filter out even numbers
            List evenNumbers = filterStream(numbers, num -> num % 2 == 0);
            System.out.println("Even numbers: " + evenNumbers);

            List words = Arrays.asList("apple", "banana", "cherry", "date");
            // Filter out words that start with 'b'
            List wordsStartingWithB = filterStream(words, word -> word.startsWith("b"));
            System.out.println("Words starting with 'b': " + wordsStartingWithB);
        }
    }
    

In this example, the filterStream() method takes a List of generic type T and a Predicate as input. The filter() method then filters the stream based on the condition provided in the predicate.

Output of the program:

    Even numbers: [2, 4, 6, 8, 10]
    Words starting with 'b': [banana]
    

Explaining the Code

The method filterStream is generic, meaning it can work with any type of list, whether it’s a list of integers, strings, or custom objects. The Predicate represents the condition used to filter the elements in the stream.

In the case of the numbers list, the filter condition is num -> num % 2 == 0, which returns only the even numbers. For the words list, the filter condition is word -> word.startsWith("b"), which returns words that start with the letter “b”.

Filtering a Stream of Custom Objects

In real-world applications, you’re often working with custom objects instead of primitive types or strings. Filtering a stream of custom objects follows the same principles, but you’ll filter based on the properties of the objects.

Example: Filtering a Stream of Employee Objects

Let’s say we have a list of Employee objects, and we want to filter out employees who have a salary greater than a certain threshold.

    import java.util.*;
    import java.util.stream.*;

    class Employee {
        String name;
        double salary;

        Employee(String name, double salary) {
            this.name = name;
            this.salary = salary;
        }

        public double getSalary() {
            return salary;
        }

        @Override
        public String toString() {
            return name + " ($" + salary + ")";
        }
    }

    public class EmployeeFilter {
        public static  List filterStream(List list, Predicate condition) {
            return list.stream()
                    .filter(condition)
                    .collect(Collectors.toList());
        }

        public static void main(String[] args) {
            List employees = Arrays.asList(
                new Employee("Alice", 50000),
                new Employee("Bob", 75000),
                new Employee("Charlie", 60000),
                new Employee("David", 120000)
            );

            // Filter employees with salary greater than $60,000
            List highEarners = filterStream(employees, emp -> emp.getSalary() > 60000);
            System.out.println("Employees with salary > 60,000: " + highEarners);
        }
    }
    

In this example, the Employee class has a getSalary() method that returns the salary of the employee. The filterStream method filters the employees based on their salary being greater than 60,000.

Output of the program:

    Employees with salary > 60,000: [Bob ($75000.0), David ($120000.0)]
    

Advanced Filtering with Multiple Conditions

Sometimes, you may need to filter a stream based on multiple conditions. You can chain multiple filter conditions together using the Predicate.and() or Predicate.or() methods.

Example: Filtering Based on Multiple Conditions

    public static  List filterStream(List list, Predicate condition) {
        return list.stream()
                .filter(condition)
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        List employees = Arrays.asList(
            new Employee("Alice", 50000),
            new Employee("Bob", 75000),
            new Employee("Charlie", 60000),
            new Employee("David", 120000)
        );

        // Filter employees who have salary > 60,000 and whose name starts with 'B'
        List filteredEmployees = filterStream(employees, emp -> emp.getSalary() > 60000 && emp.name.startsWith("B"));
        System.out.println("Filtered Employees: " + filteredEmployees);
    }
    

Output of the program:

    Filtered Employees: [Bob ($75000.0)]
    

Conclusion

Filtering streams of generic types in Java is a powerful technique that allows developers to process data more efficiently and flexibly. By utilizing the filter() method in combination with generics, you can filter streams based on various conditions—whether working with primitive data types, custom objects, or complex conditions. This approach significantly improves code readability, maintainability, and reuse.

© 2024 Tech Interview Guide. All Rights Reserved.

Please follow and like us:

Leave a Comment