Definition
- distinct() removes duplicate values so repeated maximums do not count as "second largest."
- sorted(reverseOrder()) orders values from highest to lowest.
- skip(1) passes over the single largest value.
- findFirst() returns the second-largest as an
Optional. - Without
distinct(), if the largest value appears twice,skip(1)returns another copy of the largest instead of the actual second-largest. - Returns
Optional.empty()if fewer than 2 distinct values exist in the list.
At National Bank of India, the risk desk tracks daily trade volumes: [500, 300, 500, 200, 400]. To find the second-highest volume, first remove duplicates (500 appears twice), sort descending [500, 400, 300, 200], skip the first (500), and the answer is 400.
Understanding the Concepts
How do you find the second-largest number?
package com.bank.analytics;
import java.util.*;
public class SecondLargestDemo {
public static void main(String[] args) {
List<Integer> volumes = List.of(500, 300, 500, 200, 400);
Optional<Integer> secondLargest = volumes.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
System.out.println("Second largest: " + secondLargest.orElse(-1));
}
}
Second largest: 400
Why is distinct() critical?
// WRONG: without distinct, repeated max skews the result
List<Integer> data = List.of(100, 100, 50);
Optional<Integer> wrong = data.stream()
.sorted(Comparator.reverseOrder()) // [100, 100, 50]
.skip(1) // skips one 100
.findFirst(); // returns 100 again!
Optional<Integer> correct = data.stream()
.distinct() // [100, 50]
.sorted(Comparator.reverseOrder()) // [100, 50]
.skip(1) // skips 100
.findFirst(); // returns 50 ✓
What if there is only one distinct value?
List<Integer> allSame = List.of(100, 100, 100);
Optional<Integer> result = allSame.stream()
.distinct() // [100]
.sorted(Comparator.reverseOrder()) // [100]
.skip(1) // empty
.findFirst(); // Optional.empty()
System.out.println(result.orElse(-1)); // -1
How do you generalize to Nth-largest?
// Generic Nth-largest
int n = 3;
Optional<Integer> nthLargest = volumes.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(n - 1)
.findFirst();
What is a more efficient O(n) alternative?
The sorted approach is O(n log n). For just the second-largest, a two-variable scan is O(n).
// O(n) approach: track first and second largest
int first = Integer.MIN_VALUE;
int second = Integer.MIN_VALUE;
for (int v : volumes) {
if (v > first) {
second = first;
first = v;
} else if (v > second && v != first) {
second = v;
}
}
// first=500, second=400
The stream approach is cleaner and more readable. The imperative approach is more efficient for very large datasets.
What about BigDecimal for salary?
// Second-highest salary (BigDecimal)
import java.math.BigDecimal;
List<BigDecimal> salaries = List.of(
new BigDecimal("1800000"), new BigDecimal("1000000"),
new BigDecimal("1500000"), new BigDecimal("1000000"));
Optional<BigDecimal> secondHighest = salaries.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
// Rs.1500000
What should a senior engineer know?
distinct()relies onequals()andhashCode(). ForBigDecimal, be aware thatnew BigDecimal("10.0").equals(new BigDecimal("10.00"))is false because scale differs. UsecompareToequality if needed.- The
sorted().skip(1).findFirst()pipeline on a finite stream does not short-circuit the sort: all elements must be sorted before skip can proceed. - For finding top-K elements efficiently, consider a
TreeSetwith limited size or a priority queue rather than full sorting.
Visual Overview
[500, 300, 500, 200, 400]
│
▼
distinct()
[500, 300, 200, 400]
│
▼
sorted(reverseOrder())
[500, 400, 300, 200]
│
├── skip(0).findFirst() → 500 (largest)
├── skip(1).findFirst() → 400 (2nd largest)
└── skip(2).findFirst() → 300 (3rd largest)
Key Takeaway
Second-largest uses distinct().sorted(reverseOrder()).skip(1).findFirst(). The distinct() is critical: without it, if the max value appears twice, skip(1) returns another copy of the max, not the actual second-largest distinct value. The result is Optional because there might not be two distinct values.
The pattern generalizes to Nth-largest with skip(n-1). For production use on very large datasets, the O(n) two-variable approach is more efficient than the O(n log n) sort, but the stream approach is cleaner for interview-scale data.
At National Bank of India, the risk desk finds the second-highest daily trade volume (400) from a list that contains a duplicated maximum (500 appears twice). Without distinct, the answer would incorrectly be 500 again.
Remember one sentence: distinct before sorted before skip is the pattern, and forgetting distinct is the interview trap.
---