Definition
- Highest salary:
stream().map(Employee::getSalary).max(Comparator.naturalOrder())returnsOptional. - Nth-highest unique salary:
map(getSalary).distinct().sorted(reverseOrder()).skip(n-1).findFirst()skips the first n-1 unique salary values. - distinct() is critical: if two employees earn the same highest salary, without distinct, skip(1) returns the duplicate, not the actual second-highest amount.
- Top N earners:
sorted(comparing(Employee::getSalary).reversed()).limit(n)returns Employee objects, not just salary amounts. - Per-department top earner:
groupingBy(department, collectingAndThen(maxBy(comparing(salary)), Optional::orElseThrow)).
The National Bank of India compensation committee asks three questions. "What is the highest salary?" is finding the tallest stack of coins on the table. "What is the second-highest?" means removing duplicate stacks first (if two stacks are both 18 coins tall, that is one height), then finding the next tallest. "Who are the top 3 earners?" means lining up all employees by salary and picking the first 3 people, even if two of them earn the same amount.
Understanding the Concepts
What does this question ask?
The interviewer wants you to demonstrate three related but different stream patterns: finding the maximum value, finding the Nth value in a sorted unique sequence, and selecting the top N elements. The critical subtlety is the difference between unique salary amounts and employee objects.
How do you find the highest salary?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
public class HighestSalary {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Optional<BigDecimal> highest = employees.stream()
.map(Employee::getSalary)
.max(Comparator.naturalOrder());
highest.ifPresent(s ->
System.out.println("Highest salary: Rs." + s));
}
}
Highest salary: Rs.1800000
The map step transforms Stream into Stream. The max(naturalOrder()) compares BigDecimal values using their natural ordering.
How do you find the second-highest salary?
The key is distinct() to remove duplicate salary amounts before skipping.
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
public class SecondHighestSalary {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Optional<BigDecimal> secondHighest = employees.stream()
.map(Employee::getSalary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
secondHighest.ifPresent(s ->
System.out.println("Second-highest salary: Rs." + s));
}
}
Second-highest salary: Rs.1500000
Without distinct(), if Rajesh and Amit both earn Rs.1800000, the sorted stream has two Rs.1800000 values. Skipping one gives you Rs.1800000 again, not the actual second-highest.
How do you find the Nth-highest salary?
Generalize the second-highest pattern with skip(n - 1) instead of skip(1).
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
public class NthHighestSalary {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
int n = 3;
Optional<BigDecimal> nthHighest = employees.stream()
.map(Employee::getSalary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(n - 1)
.findFirst();
nthHighest.ifPresent(s ->
System.out.println("Salary at position " + n + ": Rs." + s));
}
}
Salary at position 3: Rs.1200000
If n exceeds the number of distinct salaries, findFirst() returns Optional.empty().
How do you find the top 3 earners?
Top N earners returns Employee objects, not salary amounts. Two employees earning the same salary are two separate earners.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class Top3Earners {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
List<Employee> top3 = employees.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.limit(3)
.toList();
top3.forEach(e ->
System.out.println(e.getName() + " (SAV" + e.getAccountId()
+ "): Rs." + e.getSalary()));
}
}
Rajesh (SAV2001): Rs.1800000
Deepak (SAV2008): Rs.1500000
Amit (SAV2004): Rs.1400000
No distinct() here because you want employees, not unique salary amounts.
How is "Nth-highest salary" different from "top N earners"?
| Question | Returns | Uses distinct | Example result |
|---|---|---|---|
| Highest salary | BigDecimal | Not needed | Rs.1800000 |
| Nth-highest salary | BigDecimal | Yes | Rs.1200000 (3rd) |
| Top N earners | List
| Employee with Nth salary | Employee | Yes (on salary) | Employee earning the 3rd-highest amount |
// Nth-highest SALARY (amount)
Optional<BigDecimal> nthSalary = employees.stream()
.map(Employee::getSalary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(n - 1)
.findFirst();
// Top N EARNERS (employees)
List<Employee> topN = employees.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.limit(n)
.toList();
What are common mistakes?
Mistake 1: forgetting distinct for Nth-highest salary.
// WRONG: two employees at Rs.1800000 counted separately
Optional<BigDecimal> wrong = employees.stream()
.map(Employee::getSalary)
.sorted(Comparator.reverseOrder())
.skip(1) // skips one Rs.1800000, gets the other Rs.1800000
.findFirst(); // returns Rs.1800000, NOT the second-highest!
// CORRECT: distinct removes duplicate amounts
Optional<BigDecimal> correct = employees.stream()
.map(Employee::getSalary)
.distinct() // Rs.1800000 appears only once
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst(); // returns Rs.1500000 (actual second-highest)
Mistake 2: using limit instead of skip+findFirst for Nth.
// WRONG: limit(n) returns the first N elements, not the Nth
List<BigDecimal> wrong = employees.stream()
.map(Employee::getSalary)
.distinct()
.sorted(Comparator.reverseOrder())
.limit(3) // returns [1800000, 1500000, 1200000]
.toList();
// You still need to pick the last element
// CORRECT: skip(n-1) + findFirst gets exactly the Nth
Optional<BigDecimal> correct = employees.stream()
.map(Employee::getSalary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(2) // skip first 2
.findFirst(); // get the 3rd
How do you find per-department top earners?
Group by department first, then sort and limit within each group.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class TopEarnerPerDept {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
// Top earner per department
Map<String, Employee> topPerDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.maxBy(
Comparator.comparing(Employee::getSalary)),
Optional::orElseThrow)));
topPerDept.forEach((dept, emp) ->
System.out.println(dept + ": " + emp.getName()
+ " Rs." + emp.getSalary()));
// Top 2 earners per department
Map<String, List<Employee>> top2PerDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment))
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.limit(2)
.toList()));
top2PerDept.forEach((dept, emps) -> {
System.out.println(dept + ":");
emps.forEach(e ->
System.out.println(" " + e.getName() + " Rs." + e.getSalary()));
});
}
}
TRADING: Rajesh Rs.1800000
RISK: Deepak Rs.1500000
OPERATIONS: Suresh Rs.1100000
COMPLIANCE: Vikram Rs.1300000
What are common interview follow-up questions?
- "How do you find the employee with the Nth-highest salary?" First find the Nth salary amount, then filter employees matching that amount.
- "What if N is larger than the number of distinct salaries?"
findFirst()returnsOptional.empty(). Handle it withorElseororElseThrow. - "How do you find bottom 3 earners?" Use
sorted(comparing(salary))without.reversed(), then.limit(3). - "How do you handle null salaries?" Filter them out before sorting:
filter(e -> e.getSalary() != null).
Visual Overview
Nth-highest salary pattern:
Stream<Employee>
│
│ .map(Employee::getSalary)
▼
Stream<BigDecimal> [1800000, 1200000, 1500000, 1800000, ...]
│
│ .distinct()
▼
Stream<BigDecimal> [1800000, 1200000, 1500000, ...] (unique)
│
│ .sorted(reverseOrder())
▼
Stream<BigDecimal> [1800000, 1500000, 1400000, 1200000, ...]
│
│ .skip(n - 1) (e.g., skip(2) for 3rd)
▼
Stream<BigDecimal> [1400000, 1200000, ...]
│
│ .findFirst()
▼
Optional<BigDecimal> [1400000] ← 3rd-highest
Key Takeaway
Finding the highest salary uses map(Employee::getSalary).max(naturalOrder()), which returns Optional. For the second-highest, the critical step is distinct(): it removes duplicate salary amounts so that two employees earning the same top salary do not occupy two positions. The pattern is map(getSalary).distinct().sorted(reverseOrder()).skip(1).findFirst().
The Nth-highest generalizes to skip(n - 1).findFirst(). If N exceeds the number of distinct salaries, findFirst() returns empty. For top N earners as Employee objects, use sorted(comparing(salary).reversed()).limit(n) without distinct, because two employees earning the same salary are two separate people.
Per-department top earners combine groupingBy with maxBy(comparing(salary)) for the single top earner, or group then sort-and-limit within each group for the top N. The two-step approach (group, then post-process each group with a second stream) is the standard pattern.
Remember one sentence: distinct is the key to Nth-highest salary because it collapses duplicate amounts into one position, and the pattern is map-distinct-sorted-skip-findFirst.
---