Learnitweb

Finding the Second-Largest Number in a List Using Java Streams

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

  1. distinct():
    Removes duplicate numbers to ensure that repeated maximum values don’t interfere with finding the second-largest.
  2. sorted(Comparator.reverseOrder()):
    Sorts the stream in descending order, so the largest number comes first.
  3. skip(1):
    Skips the first element (the largest) so that the second-largest element becomes first in the resulting stream.
  4. findFirst():
    Returns the first element of the remaining stream, i.e., the second-largest.
  5. orElseThrow():
    Throws an exception if the list is empty or has no second-largest element. You could also provide a default using orElse(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

  1. Collectors.toCollection(() -> new TreeSet<>(Comparator.reverseOrder())):
    • Collects elements into a TreeSet.
    • A TreeSet naturally removes duplicates and orders elements.
    • Using Comparator.reverseOrder() ensures descending order.
  2. 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.
  3. skip(1) and findFirst():
    • 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.