How does the internal working of Stream.sorted() differ when using natural ordering versus custom comparator?

Stream.sorted() has two overloads: a no-argument version that sorts using each element's natural ordering via Comparable.compareTo(), and a Comparator-accepting version that sorts using custom comparison logic. Internally both funnel into the same sorting machinery but differ in how elements are compared.

Key Points: • Natural ordering requires stream elements to implement Comparable, otherwise a ClassCastException is thrown at runtime. • The Comparator overload lets you sort by an arbitrary field, in reverse, or by multiple criteria via thenComparing(), without touching the element's class. • Internally, sorted() is a stateful intermediate operation: for sequential streams it typically buffers all elements into an array and applies a Tim Sort-based algorithm (java.util.Arrays.sort), which is stable. • For parallel streams, sorted() may use a fork/join based parallel sort, but it still needs to gather elements before sorting, breaking pure element-at-a-time pipelining. • Because sorted() must see the whole stream before emitting anything, it cannot be used effectively with infinite streams unless combined with a prior limit().

Example: Sorting a List<Person> by name uses natural ordering if Person implements Comparable<Person>; sorting the same list by age instead uses people.stream().sorted(Comparator.comparingInt(Person::getAge)).

Code Example:

List<Person> people = getPeople();

// Natural ordering (Person implements Comparable<Person>)
people.stream().sorted().forEach(System.out::println);

// Custom comparator
people.stream()
        .sorted(Comparator.comparingInt(Person::getAge))
        .forEach(System.out::println);

Interview Tip: A concise interview answer is:

"Natural ordering relies on the element implementing Comparable and calls compareTo() internally, while the Comparator overload lets me plug in arbitrary comparison logic without changing the class. Both are stateful operations — the stream has to buffer all elements before it can sort, using a stable, Tim Sort-based algorithm under the hood, which is why sorted() doesn't work well on infinite streams without a preceding limit()."