Second Highest Salary Using Streams

Java coding interview problem for Java 8 Streams: Second Highest Salary Using Streams.

Finding the second highest salary is one of the most frequently asked Java Stream API interview problems.

This problem looks simple but tests several important concepts:

  • Sorting
  • Stream operations
  • Comparator
  • distinct values
  • Object comparison
  • Handling duplicate salaries

What is Second Highest Salary?

Given a list of employees, find the employee with the second highest salary.

Example:

Employees:

John      90000

Alice    120000

Bob       75000

David    100000

Salary ranking:

120000

100000

90000

75000

Second highest salary:

100000

Employee:

David

Understanding Ranking Problems

Finding maximum:

Highest Salary

requires:

1 comparison condition

Finding second maximum requires:

Track two values

Example:

Highest

Second Highest

During traversal:

Current Salary

        ↓

Compare with Highest

        ↓

Update ranking

Difference Between Highest and Second Highest

Example:

[50000,80000,100000]

Highest:

100000

Second Highest:

80000

But:

[50000,100000,100000]

creates a question:

Should second highest be:

100000

or:

50000

?


Most interview problems expect:

Second DISTINCT highest salary

Duplicate Salary Concept

Example:

Employees:

John      120000

Alice     120000

Bob        90000

Unique salaries:

120000

90000

Second highest salary:

90000

Therefore:

We usually apply:

distinct()

before selecting second value.


Employee Object Design

We will use:

Employee

 |
 |-- id
 |
 |-- name
 |
 |-- department
 |
 |-- salary

Employee Class

class Employee {


    private int id;


    private String name;


    private String department;


    private double salary;


    public Employee(
            int id,
            String name,
            String department,
            double salary) {


        this.id = id;

        this.name = name;

        this.department = department;

        this.salary = salary;

    }


    public int getId() {

        return id;

    }


    public String getName() {

        return name;

    }


    public String getDepartment() {

        return department;

    }


    public double getSalary() {

        return salary;

    }


    @Override
    public String toString() {


        return name +
                " - " +
                salary;

    }

}

Why This Problem is Asked in Interviews?

This problem evaluates:

1. Stream API Knowledge

Can you use:

sorted()

distinct()

skip()

findFirst()

?


2. Comparator Understanding

Can you sort objects by:

salary

?


3. Edge Case Handling

What happens when:

  • Only one employee exists?
  • Duplicate salaries exist?
  • No second highest salary exists?

4. Algorithm Optimization

Can you compare:

Sorting approach

vs

Single pass approach

?


Real-World Applications

HR Systems

Find:

Second highest paid employee

for salary analysis.


Finance Applications

Find:

Second largest transaction

E-Commerce

Find:

Second highest product price

Analytics Systems

Find:

Second highest metric value

Problem Statement

Given a list of employees, find the employee having the second highest salary using Java Streams.


Input Example

[
John IT 90000,

Alice HR 120000,

Bob Finance 75000,

David IT 100000
]

Expected Output

David - 100000

Finding Second Maximum Concept

Traditional approach:

Maintain:

highestSalary

secondHighestSalary

Example:

Input:

90000

120000

75000

100000

Start:

Highest = 0

Second = 0

Read:

90000

Update:

Highest = 90000

Read:

120000

Update:

Highest = 120000

Second = 90000

Read:

75000

No change.


Read:

100000

Update:

Second = 100000

Final:

Highest = 120000

Second = 100000

Approach 1 — Sorting Employees by Salary

The simplest solution:

  1. Sort employees by salary descending.
  2. Skip first employee.
  3. Take next employee.

Algorithm

Employee List

      ↓

Sort Salary Descending

      ↓

Skip Highest

      ↓

Get Next Employee

Java Program — Sorting Approach

import java.util.*;

public class SecondHighestSalary {


    public static Employee findSecondHighest(
            List<Employee> employees) {


        return employees.stream()

                .sorted(
                    Comparator
                    .comparing(
                        Employee::getSalary
                    )
                    .reversed()
                )

                .skip(1)

                .findFirst()

                .orElse(null);

    }

}

Dry Run

Input:

John 90000

Alice 120000

Bob 75000

David 100000

Sort descending:

Alice 120000

David 100000

John 90000

Bob 75000

Skip first:

Alice

Next:

David 100000

Result:

David

Problem With Duplicate Salary

Input:

Alice 120000

Bob 120000

David 100000

Sorted:

Alice 120000

Bob 120000

David 100000

skip(1):

returns:

Bob 120000

But expected:

David 100000

because salary should be distinct.


Need:

distinct salary handling

Approach 2 — Using Stream distinct()

Instead of sorting employees directly:

First extract salaries.


Flow:

Employees

     ↓

Salary values

     ↓

Remove duplicates

     ↓

Sort descending

     ↓

Second value

Java Program — Second Highest Salary Value

import java.util.*;

public class SecondHighestSalaryValue {


    public static Double findSecondHighestSalary(
            List<Employee> employees) {


        return employees.stream()

                .map(
                    Employee::getSalary
                )

                .distinct()

                .sorted(
                    Comparator.reverseOrder()
                )

                .skip(1)

                .findFirst()

                .orElse(null);

    }

}

Step-by-Step Explanation

Employees:

John 90000

Alice 120000

Bob 120000

David 100000

Extract salary:

90000

120000

120000

100000

distinct():

90000

120000

100000

Sort descending:

120000

100000

90000

skip first:

120000

Result:

100000

Complexity Analysis

Sorting:

O(n log n)

Stream processing:

O(n)

Total:

O(n log n)

Space:

O(n)

Advantages

  • Clean Stream solution.
  • Handles duplicate salaries.
  • Easy to understand.
  • Interview friendly.

Drawbacks

  • Requires sorting.
  • Extra memory.
  • Not optimal for huge datasets.

Finding Second Highest Employee Object Using Streams

The previous approach finds only the salary value.

Many interviews ask:

Find the employee object having the second highest salary.


Example:

Employees:

John      90000

Alice    120000

Bob       75000

David    100000

Expected:

David 100000

Stream Pipeline

Employees

    ↓

Sort by Salary Descending

    ↓

Remove duplicate salaries

    ↓

Skip Highest

    ↓

Find Employee

Java Program — Second Highest Employee

import java.util.*;

public class SecondHighestEmployee {


    public static Employee findSecondHighest(
            List<Employee> employees) {


        return employees.stream()

                .sorted(
                    Comparator
                    .comparing(
                        Employee::getSalary
                    )
                    .reversed()
                )

                .filter(
                    employee ->
                    employees.stream()
                    .filter(
                        e ->
                        e.getSalary()
                        ==
                        employee.getSalary()
                    )
                    .count()
                    >= 1
                )

                .skip(1)

                .findFirst()

                .orElse(null);

    }

}

Better Approach Using Salary Set

A cleaner approach:

  1. Find second highest salary.
  2. Find employee with that salary.

Step 1 — Find Second Highest Salary

double secondHighestSalary =

employees.stream()

.map(
    Employee::getSalary
)

.distinct()

.sorted(
    Comparator.reverseOrder()
)

.skip(1)

.findFirst()

.orElse(0);

Step 2 — Find Employee

Employee result =

employees.stream()

.filter(
    employee ->
    employee.getSalary()
    ==
    secondHighestSalary
)

.findFirst()

.orElse(null);

Why Separate Steps?

It separates responsibilities:

Find Ranking

      ↓

Find Object

This is easier to debug and maintain.


Using Comparator Without Sorting

Sorting is:

O(n log n)

For only finding second highest:

we can use:

Single Traversal

Single Pass Algorithm

Maintain:

highestSalary

secondHighestSalary

Java Program — O(n) Solution

public class SecondHighestSinglePass {


    public static Employee findSecondHighest(
            List<Employee> employees) {


        Employee highest = null;

        Employee secondHighest = null;


        for(Employee employee : employees) {


            if(highest == null ||
               employee.getSalary()
               >
               highest.getSalary()) {


                secondHighest = highest;

                highest = employee;

            }


            else if(
                (secondHighest == null ||
                employee.getSalary()
                >
                secondHighest.getSalary())

                &&

                employee.getSalary()
                <
                highest.getSalary()
            ) {


                secondHighest = employee;

            }

        }


        return secondHighest;

    }

}

Dry Run — Single Pass

Input:

John 90000

Alice 120000

Bob 75000

David 100000

Initial:

Highest = null

Second = null

Read John:

90000

Update:

Highest = John

Read Alice:

120000

Update:

Highest = Alice

Second = John

Read Bob:

75000

No change.


Read David:

100000

Compare:

100000 < 120000

Update:

Second = David

Final:

Highest = Alice

Second = David

Complexity Analysis

Each employee is processed once.

Time:

O(n)

Space:

O(1)

PriorityQueue Approach

For large datasets, another approach is:

Min Heap

Maintain:

Top 2 salaries

Example:

Employees:

90000

120000

75000

100000

Heap size:

2

Final heap:

100000

120000

Java Program

import java.util.*;

public class SecondHighestUsingHeap {


    public static Employee findSecondHighest(
            List<Employee> employees) {


        PriorityQueue<Employee> heap =

                new PriorityQueue<>(

                Comparator.comparing(
                    Employee::getSalary
                )

                );


        for(Employee employee :
                employees) {


            heap.offer(employee);


            if(heap.size() > 2) {

                heap.poll();

            }

        }


        return heap.peek();

    }

}

Complexity

For each insertion:

O(log k)

where:

k = 2

Total:

O(n log k)

Since:

k = 2

approximately:

O(n)

Space:

O(k)

Finding Second Highest Salary Per Department

Another very common enterprise question:

Find second highest paid employee in each department.


Example:

Input:

John IT 90000

Bob IT 120000

Mike IT 100000

Alice HR 80000

David HR 100000

Output:

IT → Mike 100000

HR → Alice 80000

Approach

Group Employees

        ↓

Sort Each Group

        ↓

Skip First

        ↓

Return Second

Java Program

import java.util.*;
import java.util.stream.Collectors;


Map<String,Employee> result =


employees.stream()

.collect(

Collectors.groupingBy(

Employee::getDepartment,

Collectors.collectingAndThen(

Collectors.toList(),

list -> list.stream()

.sorted(

Comparator
.comparing(
    Employee::getSalary
)
.reversed()

)

.skip(1)

.findFirst()

.orElse(null)

)

)

);

Explanation

First group:

IT

[
John 90000,

Bob 120000,

Mike 100000
]

Sort:

120000

100000

90000

Skip first:

100000

Result:

Mike

Sorting vs Stream vs Heap Comparison

Approach Time Space Best Use
Sorting O(n log n) O(n) Simple solutions
Stream sorted() O(n log n) O(n) Modern Java
Single Pass O(n) O(1) Best performance
PriorityQueue O(n log k) O(k) Large datasets

Handling Null Values

Production data may contain:

Employee = null

Salary = null

Ignore Null Employees

employees.stream()

.filter(
    Objects::nonNull
)

Handle Null Salary

Comparator<Employee> comparator =

Comparator.comparing(

Employee::getSalary,

Comparator.nullsLast(
    Double::compare
)

);

Comparable vs Comparator

Comparable

Used for natural ordering.

Example:

Employee default sorting

Comparator

Used for custom sorting.

Example:

Salary ranking

Common Interview Mistakes

Mistake 1

Using:

skip(1)

without:

distinct salary

Problem:

120000

120000

100000

returns wrong result.


Mistake 2

Not handling fewer than two employees.

Example:

[John]

No second highest exists.


Mistake 3

Sorting entire list unnecessarily.

For maximum performance:

Use:

Single Pass

Mistake 4

Ignoring duplicate salaries.

Clarify:

Second employee?

or

Second distinct salary?

Edge Cases

Case Handling
Empty list Return null/Optional
One employee No second salary
Same salary for all No second distinct salary
Duplicate salaries Use distinct()
Null values Filter/handle

Interview Follow-up Questions

Q1. Find highest salary employee.

Q2. Find second highest salary.

Q3. Find third highest salary.

Q4. Find Nth highest salary.

Q5. Find second highest salary by department.

Q6. Difference between sorting and heap.

Q7. How to solve without sorting?


Related Java Collection Problems

  • Find Employees With Highest Salary
  • Sort Employees by Salary
  • Group Employees by Department
  • Custom Comparator Examples
  • Convert List to Map
  • Top K Frequent Elements

Key Takeaways

Second highest salary follows:

Employee List

        ↓

Find Ranking

        ↓

Handle Duplicates

        ↓

Return Employee

Recommended solutions:

Interview Friendly

Use:

Stream

+

distinct()

+

sorted()

+

skip()

Production Performance

Use:

Single Pass O(n)

Large Data

Use:

PriorityQueue

Complexities:

Stream sorting:

O(n log n)

Single pass:

O(n)

Heap:

O(n log k)

Frequently Asked Interview Questions

Q1. Why use distinct()?

To ignore duplicate salaries.


Q2. Why not simply skip(1)?

Because duplicate highest salaries may exist.


Q3. What is the optimal solution?

Single traversal with two variables.


Q4. How to find second highest per department?

Use:

groupingBy()

+

sorting/max logic

Interview Tip

When asked:

"Find second highest salary using Streams."

Explain:

  1. Extract salary values.
  2. Remove duplicates using distinct().
  3. Sort descending.
  4. Skip highest salary.
  5. Get next value.
  6. For object retrieval, filter employees by salary.

For senior Java interviews, discuss:

  • Stream pipeline design.
  • Comparator usage.
  • Duplicate handling.
  • Complexity trade-offs.
  • Single-pass optimization.

This demonstrates strong understanding of Java Streams, Collections, and real-world ranking problems.