Back to Stream: filter
Q14Question 14 of 19

Filter employees whose salary is between 40,000 and 80,000 (inclusive range)

Key Concepts
  • An inclusive range check uses >= for the lower bound and <= for the upper bound, so both endpoints qualify.
  • For money, use BigDecimal.compareTo: >= 0 for at-least, <= 0 for at-most, never double comparisons.
  • Getting the boundary wrong (exclusive vs inclusive) is one of the most common interview and production mistakes.
  • Define bounds as named constants to avoid magic numbers scattered across the codebase.
  • Extract the range predicate into a reusable function because the same band logic applies to loan tiers and tax slabs.
  • Predicate combinators and() compose the two boundary checks cleanly.
  • In production, push range conditions to database WHERE clauses for large datasets.

Definition

An inclusive range filter keeps values where salary is at least the lower bound and at most the upper bound, endpoints included, using BigDecimal.compareTo for precision.
DIAGRAM
- **Inclusive range** – `salary.compareTo(low) >= 0 && salary.compareTo(high) <= 0` keeps both endpoints.
- **BigDecimal for money** – never use `double` for boundary comparisons; floating-point loses precision.
- **Named constants** – define `LOW = new BigDecimal("40000")` and `HIGH = new BigDecimal("80000")`.
- **Reusable predicate** – `Predicate<Employee> inBand = e -> e.salary().compareTo(low) >= 0 && e.salary().compareTo(high) <= 0`.
- **Boundary precision** – inclusive means exactly 40,000 and exactly 80,000 both qualify.

The HR manager at National Bank of India targets a mid-income personal-loan offer at employees earning between Rs.40,000 and Rs.80,000 inclusive. Someone earning exactly Rs.40,000 qualifies because the band includes both ends. Someone earning Rs.90,000 is out.

Understanding the Concepts

How do you filter a range with BigDecimal?

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.List;

public class RangeFilterDemo {
    record Employee(String name, String department, BigDecimal salary) {}

    public static void main(String[] args) {
        BigDecimal LOW = new BigDecimal("40000");
        BigDecimal HIGH = new BigDecimal("80000");

        List<Employee> staff = List.of(
            new Employee("Rajesh", "IT", new BigDecimal("85000")),
            new Employee("Priya", "Finance", new BigDecimal("72000")),
            new Employee("Amit", "IT", new BigDecimal("40000")),
            new Employee("Deepa", "HR", new BigDecimal("30000")),
            new Employee("Vikram", "Finance", new BigDecimal("80000")),
            new Employee("Neha", "Sales", new BigDecimal("55000"))
        );

        List<Employee> midBand = staff.stream()
            .filter(e -> e.salary().compareTo(LOW) >= 0
                      && e.salary().compareTo(HIGH) <= 0)
            .toList();

        midBand.forEach(e -> System.out.println(e.name() + ": Rs." + e.salary()));
    }
}
TEXT
Priya: Rs.72000
Amit: Rs.40000
Vikram: Rs.80000
Neha: Rs.55000

Amit (exactly 40,000) and Vikram (exactly 80,000) both qualify because the range is inclusive.

Why must you use BigDecimal for money ranges?

JAVA
// WRONG: double loses precision at boundaries
double salary = 40000.00;
System.out.println(salary >= 40000.00); // may be unreliable for edge values

// CORRECT: BigDecimal compares exactly
BigDecimal salary = new BigDecimal("40000.00");
System.out.println(salary.compareTo(new BigDecimal("40000")) >= 0); // true, always correct

What is the difference between inclusive and exclusive ranges?

DIAGRAM
| Range type | Lower | Upper | 40,000 in? | 80,000 in? |
|------------|-------|-------|------------|------------|
| Inclusive `[40K, 80K]` | `>= 0` | `<= 0` | Yes | Yes |
| Exclusive `(40K, 80K)` | `> 0` | `< 0` | No | No |
| Half-open `[40K, 80K)` | `>= 0` | `< 0` | Yes | No |
| Half-open `(40K, 80K]` | `> 0` | `<= 0` | No | Yes |

How do you extract a reusable range predicate?

JAVA
import java.math.BigDecimal;
import java.util.function.Predicate;

public class SalaryPredicates {
    public static Predicate<Employee> salaryBetween(BigDecimal low, BigDecimal high) {
        return e -> e.salary().compareTo(low) >= 0
                  && e.salary().compareTo(high) <= 0;
    }
}

// Usage:
List<Employee> midBand = staff.stream()
    .filter(SalaryPredicates.salaryBetween(
        new BigDecimal("40000"), new BigDecimal("80000")))
    .toList();

The same range logic applies to loan tiers, tax slabs, and bonus bands.

What are common mistakes?

JAVA
// WRONG: using exclusive when inclusive is required
staff.stream().filter(e -> e.salary().compareTo(LOW) > 0
                        && e.salary().compareTo(HIGH) < 0);
// Excludes exactly 40,000 and exactly 80,000!

// CORRECT: inclusive boundaries
staff.stream().filter(e -> e.salary().compareTo(LOW) >= 0
                        && e.salary().compareTo(HIGH) <= 0);

// WRONG: using double for money
staff.stream().filter(e -> e.getSalaryDouble() >= 40000 && e.getSalaryDouble() <= 80000);

// CORRECT: using BigDecimal
staff.stream().filter(e -> e.salary().compareTo(LOW) >= 0 && e.salary().compareTo(HIGH) <= 0);

What are edge cases?

  • Exactly on lower bound: Rs.40,000 qualifies with >= 0.
  • Exactly on upper bound: Rs.80,000 qualifies with <= 0.
  • Below lower bound: Rs.30,000 fails the first check.
  • Above upper bound: Rs.85,000 fails the second check.
  • Null salary: guard with e.salary() != null && before compareTo.
  • Inverted bounds: if low > high, no element can satisfy both conditions.

What about using Predicate combinators?

JAVA
Predicate<Employee> atLeast40K = e -> e.salary().compareTo(new BigDecimal("40000")) >= 0;
Predicate<Employee> atMost80K = e -> e.salary().compareTo(new BigDecimal("80000")) <= 0;

List<Employee> midBand = staff.stream()
    .filter(atLeast40K.and(atMost80K))
    .toList();

What should a senior engineer know?

  • Define bounds as named constants or configuration values to avoid magic numbers.
  • The same mid-band logic reappears for loan eligibility, tax slabs, and bonus tiers. Extract it into a reusable utility.
  • In production, push range conditions to database WHERE clauses for large employee tables.
  • BigDecimal scale matters: new BigDecimal("40000") and new BigDecimal("40000.00") are equal by compareTo but not by equals. Use compareTo for range checks.

Visual Overview

DIAGRAM
  Inclusive range filter [40000, 80000]:
  ─────────────────────────────────────
  Rajesh:  85000  >= 40K ✓  <= 80K ✗ → discard
  Priya:   72000  >= 40K ✓  <= 80K ✓ → keep
  Amit:    40000  >= 40K ✓  <= 80K ✓ → keep (boundary included)
  Deepa:   30000  >= 40K ✗          → discard (short-circuit)
  Vikram:  80000  >= 40K ✓  <= 80K ✓ → keep (boundary included)
  Neha:    55000  >= 40K ✓  <= 80K ✓ → keep

  Result: [Priya, Amit, Vikram, Neha]

Key Takeaway

An inclusive range check joins two comparisons with AND: the value must be at least the lower bound and at most the upper bound. Both endpoints qualify. Getting the boundary wrong is one of the most common interview mistakes and in banking could wrongly include or exclude a customer from a product tier.

Always use BigDecimal and its compareTo method for money ranges. Double and float lose precision at boundaries. Define bounds as named constants and extract the range predicate into a reusable function because the same band logic applies to loan tiers, tax slabs, and bonus bands.

BigDecimal.compareTo returns >= 0 for at-least and <= 0 for at-most. In production, push range conditions to database WHERE clauses for large datasets.

Remember one sentence: inclusive range uses compareTo >= 0 for the lower bound and <= 0 for the upper bound, and always use BigDecimal for money.

---

Share
Use ← → arrow keys to navigate