In many applications, you may need to identify the second-largest (or second-highest) number in a collection. Java Streams provide elegant and concise ways to achieve this with functional-style operations.
Consider the example list:
List<Integer> nums = Arrays.asList(5, 9, 1, 9, 3, 7);
Notice that the list may contain duplicates, so we need to handle that to ensure correctness.
Approach 1: Using distinct(), sorted(), and skip()
import java.util.*;
import java.util.stream.*;
public class SecondLargestExample {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(5, 9, 1, 9, 3, 7);
int second = nums.stream()
.distinct() // Remove duplicates
.sorted(Comparator.reverseOrder()) // Sort in descending order
.skip(1) // Skip the largest element
.findFirst() // Get the next element (second-largest)
.orElseThrow(); // Throw exception if empty
System.out.println(second); // Output: 7
}
}
Explanation
distinct():
Removes duplicate numbers to ensure that repeated maximum values don’t interfere with finding the second-largest.sorted(Comparator.reverseOrder()):
Sorts the stream in descending order, so the largest number comes first.skip(1):
Skips the first element (the largest) so that the second-largest element becomes first in the resulting stream.findFirst():
Returns the first element of the remaining stream, i.e., the second-largest.orElseThrow():
Throws an exception if the list is empty or has no second-largest element. You could also provide a default usingorElse(defaultValue)if desired.
Advantages:
- Simple and readable.
- Handles duplicates automatically.
- Fully uses the functional stream API.
Approach 2: Using a Reversed TreeSet and collectingAndThen
import java.util.*;
import java.util.stream.*;
public class SecondLargestExample {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(5, 9, 1, 9, 3, 7);
int second = nums.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.reverseOrder())),
set -> set.stream()
.skip(1)
.findFirst()
.orElseThrow()
));
System.out.println(second); // Output: 7
}
}
Explanation
Collectors.toCollection(() -> new TreeSet<>(Comparator.reverseOrder())):- Collects elements into a TreeSet.
- A
TreeSetnaturally removes duplicates and orders elements. - Using
Comparator.reverseOrder()ensures descending order.
collectingAndThen():- Allows a post-processing step on the result of a collector.
- In this case, after creating the TreeSet, we stream over it to skip the largest and get the second-largest.
skip(1)andfindFirst():- Same as the first approach: skip the largest and select the next element.
Advantages:
- Ensures uniqueness and sorted order in a single collection operation.
- Handles larger data sets efficiently with
TreeSet. - Clear separation of collection and post-processing logic.
