Filtering Operations

Java 8’s Stream API provides powerful filtering operations to process collections in a functional, declarative way. The primary method for filtering is filter(), which selects elements based on a condition defined by a Predicate.
 
  • Stream: A sequence of elements supporting functional-style operations.
  • filter(): An intermediate operation that takes a Predicate (condition) and returns a new stream with elements that satisfy the condition.
  • Predicate: A functional interface (java.util.function.Predicate) with a test() method returning true or false.
  • Terminal Operation: Required to produce a result (e.g., collect(), forEach(), findFirst()).

Syntax

stream.filter(predicate).terminalOperation();

predicate: A lambda expression or method reference (e.g., x -> x > 5).
terminalOperation: Converts the filtered stream into a result (e.g., collect(Collectors.toList())).Code language: CSS (css)

Filtering Operations

  1. Single filter(): Select elements based on one condition.
  2. Chained filter(): Apply multiple conditions sequentially.
  3. Combined Predicates: Use Predicate methods (and(), or(), negate()) for complex logic.
  4. Short-Circuiting: Use operations like findFirst() or findAny() to stop after finding a match.

1. Single Filter

Filter a list to keep only numbers greater than 5:

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

public class SingleFilter {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(2, 7, 4, 9, 1, 6);
        List<Integer> greaterThanFive = numbers.stream()
                                              .filter(n -> n > 5)
                                              .collect(Collectors.toList());
        System.out.println(greaterThanFive); // Output: [7, 9, 6]
    }
}

2. Chained Filters

Filter strings that start with “A” and have length greater than 3:

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

public class ChainedFilters {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Ananth", "Raj", "Aravind", "Abi");
        List<String> filteredNames = names.stream()
                                        .filter(s -> s.startsWith("A"))
                                        .filter(s -> s.length() > 3)
                                        .collect(Collectors.toList());
        System.out.println(filteredNames); // Output: [Ananth, Aravind]
    }
}

3. Combined Predicates

Filter numbers that are even and less than 10:

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

public class CombinedPredicates {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(2, 3, 4, 8, 12, 6, 9);
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<Integer> lessThanTen = n -> n < 10;

        List<Integer> result = numbers.stream()
                                     .filter(isEven.and(lessThanTen))
                                     .collect(Collectors.toList());
        System.out.println(result); // Output: [2, 4, 8, 6]
    }
}

4. Filtering Objects with Short-Circuiting

Filter a list of Employee objects to find the first employee with a salary above 50000:

import java.util.Arrays;
import java.util.List;

class Employee {
    String name;
    double salary;

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

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

public class ObjectFiltering {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Aravind", 45000),
            new Employee("Akhil", 60000),
            new Employee("Amruth", 70000)
        );

        Employee highEarner = employees.stream()
                                      .filter(e -> e.salary > 50000)
                                      .findFirst()
                                      .orElse(null);
        System.out.println(highEarner); // Output: Akhil(60000)
    }
}

Additional Filtering Operations

Additional Filtering Operations

  1. Distinct Filtering (distinct()): Removes duplicates from a stream, effectively filtering out non-unique elements.
  2. Limit and Skip (limit(), skip()): Filters a stream by restricting the number of elements (limit()) or skipping initial elements (skip()).
  3. Filtering with Complex Predicates: Use custom or combined predicates for sophisticated filtering logic.
  4. Filtering with takeWhile() and dropWhile(): Conditionally take or drop elements based on a predicate (introduced in Java 9, but relevant for modern Java).
  5. Filtering with Mapping (filter() + map()): Combine filtering with transformation to process data.
  6. Short-Circuiting Filters (findAny(), findFirst(), anyMatch(), allMatch(), noneMatch()): Optimize filtering by stopping early when a condition is met.

Examples

1. Distinct Filtering

Remove duplicate elements from a list:

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

public class DistinctFiltering {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Charlie", "Bob");
        List<String> uniqueNames = names.stream()
                                       .distinct()
                                       .collect(Collectors.toList());
        System.out.println(uniqueNames); // Output: [Alice, Bob, Charlie]
    }
}

2. Limit and Skip

  • Limit: Filter to keep only the first n elements.
  • Skip: Filter out the first n elements.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LimitSkipFiltering {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);

        // Keep only the first 3 elements
        List<Integer> limited = numbers.stream()
                                      .limit(3)
                                      .collect(Collectors.toList());
        System.out.println(limited); // Output: [1, 2, 3]

        // Skip the first 3 elements
        List<Integer> skipped = numbers.stream()
                                      .skip(3)
                                      .collect(Collectors.toList());
        System.out.println(skipped); // Output: [4, 5, 6, 7, 8]

        // Combine limit and skip
        List<Integer> combined = numbers.stream()
                                       .skip(2)
                                       .limit(3)
                                       .collect(Collectors.toList());
        System.out.println(combined); // Output: [3, 4, 5]
    }
}

3. Complex Predicates

Filter a list of objects based on multiple conditions using a custom predicate:

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

class Product {
    String name;
    double price;
    boolean inStock;

    Product(String name, double price, boolean inStock) {
        this.name = name;
        this.price = price;
        this.inStock = inStock;
    }

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

public class ComplexPredicateFiltering {
    public static void main(String[] args) {
        List<Product> products = Arrays.asList(
            new Product("Laptop", 1000.0, true),
            new Product("Phone", 500.0, false),
            new Product("Tablet", 300.0, true),
            new Product("Monitor", 200.0, true)
        );

        Predicate<Product> isAffordable = p -> p.price < 600.0;
        Predicate<Product> isAvailable = p -> p.inStock;

        List<Product> affordableAvailable = products.stream()
                                                  .filter(isAffordable.and(isAvailable))
                                                  .collect(Collectors.toList());
        System.out.println(affordableAvailable); // Output: [Tablet(300.0), Monitor(200.0)]
    }
}

4. takeWhile and dropWhile (Java 9+)

  • takeWhile(): Takes elements while the predicate is true, stopping at the first false.
  • dropWhile(): Drops elements while the predicate is true, then takes the rest.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TakeDropWhileFiltering {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(2, 4, 6, 7, 8, 10);

        // Take elements while they are even
        List<Integer> taken = numbers.stream()
                                    .takeWhile(n -> n % 2 == 0)
                                    .collect(Collectors.toList());
        System.out.println(taken); // Output: [2, 4, 6]

        // Drop elements while they are even
        List<Integer> dropped = numbers.stream()
                                      .dropWhile(n -> n % 2 == 0)
                                      .collect(Collectors.toList());
        System.out.println(dropped); // Output: [7, 8, 10]
    }
}

5. Filtering with Mapping

Filter and transform data in one pipeline (e.g., extract names of valid users):

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

class User {
    String name;
    boolean active;

    User(String name, boolean active) {
        this.name = name;
        this.active = active;
    }
}

public class FilterMap {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", true),
            new User("Bob", false),
            new User("Charlie", true)
        );

        List<String> activeUserNames = users.stream()
                                           .filter(u -> u.active)
                                           .map(u -> u.name)
                                           .collect(Collectors.toList());
        System.out.println(activeUserNames); // Output: [Alice, Charlie]
    }
}

6. Short-Circuiting Filters

Use anyMatch(), allMatch(), or noneMatch() to check conditions without collecting all results:

import java.util.Arrays;
import java.util.List;

public class ShortCircuitFiltering {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Check if any number is even
        boolean hasEven = numbers.stream()
                                .anyMatch(n -> n % 2 == 0);
        System.out.println("Has even number: " + hasEven); // Output: Has even number: true

        // Check if all numbers are positive
        boolean allPositive = numbers.stream()
                                    .allMatch(n -> n > 0);
        System.out.println("All positive: " + allPositive); // Output: All positive: true

        // Check if no number is negative
        boolean noNegative = numbers.stream()
                                   .noneMatch(n -> n < 0);
        System.out.println("No negative: " + noNegative); // Output: No negative: true

        // Find any number greater than 3
        Integer anyGreaterThanThree = numbers.stream()
                                            .filter(n -> n > 3)
                                            .findAny()
                                            .orElse(null);
        System.out.println("Any > 3: " + anyGreaterThanThree); // Output: Any > 3: 4 (or another number > 3)
    }
}

Best Practices

  • Use distinct() sparingly, as it may require additional memory for tracking unique elements.
  • Combine limit() and skip() for pagination-like scenarios.
  • Prefer takeWhile()/dropWhile() for ordered streams with conditional breaks.
  • Use short-circuiting operations to avoid unnecessary processing.

Filtering operations in Java Streams provide a powerful mechanism for selecting elements based on specific criteria. By leveraging predicates and chaining methods like filter(), distinct(), limit(), and skip(), developers can efficiently process data streams while maintaining clean and expressive code. This approach enhances productivity and readability, making Java Streams a valuable tool for modern data processing tasks.

Scroll to Top