Back to Stream: filter
Q13Question 13 of 19

Filter male employees older than 30 from the "Sales" department

Key Concepts
  • Chain three conditions with logical AND so an element passes only when all three are true (gender, age, department).
  • Java short-circuits AND left to right: put the cheapest and most selective check first for performance.
  • Older than 30 means strictly greater than 30: someone exactly 30 does not qualify.
  • Compare Strings with equals() and put the constant on the left for null safety.
  • Named Predicate constants make multi-condition policies readable and testable in isolation.
  • In production, prefer enums for gender and department over raw Strings to avoid typo bugs.
  • For large datasets, filter-order optimization matters because short-circuiting skips unnecessary checks.

Definition

Chain three conditions with AND so an element passes only when gender is male, age is over 30, and department is Sales, all simultaneously true.
  • Three-way AND – "M".equals(gender) && age > 30 && "Sales".equals(dept) keeps only elements where all three conditions hold.
  • Short-circuit – Java evaluates left to right and stops at the first false, skipping remaining checks.
  • Boundary – 'older than 30' means strictly greater than 30; exactly 30 does not qualify.
  • Null safety – "Sales".equals(dept) returns false on null department instead of throwing NPE.
  • Named predicates – extract each condition into a Predicate constant for reuse and readability.

The HR manager at National Bank of India has three rules at the door: 'You must be male, AND older than 30, AND in the Sales department.' Only a person satisfying every rule walks through. Fail any single rule and you are stopped.

Understanding the Concepts

How do you filter with three conditions?

JAVA
package com.bank.service;

import java.util.List;

public class ThreeConditionFilter {
    record Employee(String name, String gender, int age, String department, double salary) {}

    public static void main(String[] args) {
        List<Employee> staff = List.of(
            new Employee("Rajesh", "M", 35, "Sales", 85000),
            new Employee("Priya", "F", 28, "Sales", 72000),
            new Employee("Amit", "M", 42, "IT", 95000),
            new Employee("Deepa", "F", 55, "Sales", 110000),
            new Employee("Vikram", "M", 30, "Sales", 78000),
            new Employee("Suresh", "M", 38, "Sales", 92000)
        );

        List<Employee> result = staff.stream()
            .filter(e -> "M".equals(e.gender())
                      && e.age() > 30
                      && "Sales".equals(e.department()))
            .toList();

        result.forEach(e -> System.out.println(
            e.name() + " (age " + e.age() + ", " + e.department() + ")"));
    }
}
TEXT
Rajesh (age 35, Sales)
Suresh (age 38, Sales)

Vikram is male and in Sales but is exactly 30, which does not satisfy strictly greater than 30.

Why does condition order matter for performance?

Java short-circuits AND left to right. If most employees are not in Sales, putting the department check first means gender and age checks are skipped for the majority.

JAVA
// Optimized order: most selective first
staff.stream().filter(e -> "Sales".equals(e.department())
                        && "M".equals(e.gender())
                        && e.age() > 30);

How do you use named predicates?

JAVA
import java.util.function.Predicate;

Predicate<Employee> isMale = e -> "M".equals(e.gender());
Predicate<Employee> olderThan30 = e -> e.age() > 30;
Predicate<Employee> inSales = e -> "Sales".equals(e.department());

// Compose with and()
List<Employee> result = staff.stream()
    .filter(isMale.and(olderThan30).and(inSales))
    .toList();
// Reads like: "is male AND older than 30 AND in Sales"

Named predicates are testable in isolation and reusable across services.

What is the comparison table for approaches?

DIAGRAM
| Approach | Code | Pros | Cons |
|----------|------|------|------|
| Single inline lambda | `e -> A && B && C` | Compact | Not reusable |
| Chained filters | `.filter(A).filter(B).filter(C)` | Readable | Verbose |
| Named predicates + and() | `predA.and(predB).and(predC)` | Reusable, testable | Slightly more setup |

What are common mistakes?

JAVA
// WRONG: using == for String comparison
staff.stream().filter(e -> e.gender() == "M"); // reference comparison

// CORRECT: using equals with constant on the left
staff.stream().filter(e -> "M".equals(e.gender()));

// WRONG: inclusive boundary when exclusive is needed
staff.stream().filter(e -> e.age() >= 30); // includes exactly 30

// CORRECT: strictly older than 30
staff.stream().filter(e -> e.age() > 30);

What about using enums instead of Strings?

JAVA
enum Gender { M, F }
enum Department { IT, SALES, FINANCE, HR }

record Employee(String name, Gender gender, int age, Department department) {}

// Enum comparison is safe with == because enums are singletons
staff.stream().filter(e -> e.gender() == Gender.M
                        && e.age() > 30
                        && e.department() == Department.SALES);

Enums eliminate typo bugs like "Saels" and enable IDE auto-completion.

What are edge cases?

  • No matches: if no male employee over 30 exists in Sales, the result is an empty list.
  • Null fields: if gender or department can be null, the constant-on-left equals pattern prevents NPE.
  • Age boundary: exactly 30 does not qualify for 'older than 30'. Clarify inclusive vs exclusive in requirements.

What should a senior engineer know?

  • Extract multi-condition policies into named Predicate constants for readability and unit testing.
  • In production banking systems, prefer enums for categorical fields to eliminate String typo bugs.
  • For very large employee tables, push conditions to the database with WHERE clauses instead of in-memory filter.
  • The order of AND conditions affects performance: put the cheapest and most selective check first.

Visual Overview

DIAGRAM
  Three-condition filter:
  ───────────────────────
  Source: [Rajesh:M:35:Sales, Priya:F:28:Sales, Amit:M:42:IT,
           Deepa:F:55:Sales, Vikram:M:30:Sales, Suresh:M:38:Sales]
       │
  filter(gender==M && age>30 && dept==Sales)
       │
  Rajesh:  M ✓  35>30 ✓  Sales ✓ → keep
  Priya:   F ✗  (short-circuit)  → discard
  Amit:    M ✓  42>30 ✓  IT ✗   → discard
  Deepa:   F ✗  (short-circuit)  → discard
  Vikram:  M ✓  30>30 ✗         → discard (not strictly older)
  Suresh:  M ✓  38>30 ✓  Sales ✓ → keep
       │
  Result: [Rajesh, Suresh]

Key Takeaway

When several conditions must all hold, join them with logical AND in a single predicate or use named predicates composed with and(). Each element survives only when every test returns true. Java short-circuits left to right, so the second and third checks are skipped when an earlier check fails.

The boundary detail matters: 'older than 30' means strictly greater than 30, so exactly 30 does not qualify. Always compare Strings with equals() and put the constant on the left side for null safety. In production, prefer enums for categorical fields to eliminate typo bugs.

For large datasets, the order of conditions affects performance: put the cheapest and most selective check first. Named predicates make multi-condition policies readable, testable, and reusable across services.

Remember one sentence: combine all conditions with AND, put the most selective check first for performance, and be precise about boundary inclusivity.

---

Share
Use ← → arrow keys to navigate