Definition
- 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
Predicateconstant 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?
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() + ")"));
}
}
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.
// 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?
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?
| 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?
// 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?
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
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.
---