🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › Comparator.html
Comparator (Java Platform SE 8 )
3 weeks ago - default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T,? extends U> keyExtractor)
🌐
Baeldung
baeldung.com › home › java › guide to java comparator.comparing()
Guide to Java Comparator.comparing() | Baeldung
January 8, 2024 - Similarly, the functions thenComparingLong and thenComparingDouble are for using long and double sorting keys, respectively. This article is a guide to several features introduced in Java 8 for the Comparator interface.
🌐
How to do in Java
howtodoinjava.com › home › java sorting › java comparator thencomparing() example
Java Comparator thenComparing() Example - HowToDoInJava
August 30, 2022 - private static ArrayList<Employee> getUnsortedEmployeeList() { ArrayList<Employee> list = new ArrayList<>(); list.add( new Employee(2, "Lokesh", "Gupta") ); list.add( new Employee(1, "Alex", "Gussin") ); list.add( new Employee(4, "Brian", "Sux") ); list.add( new Employee(5, "Neon", "Piper") ); list.add( new Employee(3, "David", "Beckham") ); list.add( new Employee(7, "Alex", "Beckham") ); list.add( new Employee(6, "Brian", "Suxena") ); return list; } ArrayList<Employee> employees = getUnsortedEmployeeList(); //Compare by first name and then last name Comparator<Employee> compareByName = Comparator .comparing(Employee::getFirstName) .thenComparing(Employee::getLastName); Collections.sort(employees, compareByName);
🌐
Medium
medium.com › @AlexanderObregon › javas-comparator-thencomparing-method-explained-988e8f926a64
Java’s Comparator.thenComparing() Explained | Medium
November 6, 2024 - The sequence of criteria is key in multi-level sorting. Place your primary comparison first, followed by secondary, tertiary, and other criteria. For example, sorting a list of products by category and, within each category, by price:
🌐
Blogger
javarevisited.blogspot.com › 2021 › 09 › comparator-comparing-thenComparing-example-java-.html
Java 8 Comparator comparing() and thenComparing() Example - Tutorial
May 24, 2023 - As the name suggests, you can use ... fields and multiple orders. For example, you can use comparing() and thenComparing() to compare Book objects first on author and then price....
🌐
Oracle
docs.oracle.com › en › java › javase › 26 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 26 & JDK 26)
3 weeks ago - API Note: For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code, Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER); Parameters: ...
🌐
Tabnine
tabnine.com › home page › code › java › java.util.comparator
Java Examples & Tutorials of Comparator.thenComparing (java.util) | Tabnine
if (mediaTypes.size() > 1) { mediaTypes.sort(MediaType.SPECIFICITY_COMPARATOR.thenComparing(MediaType.QUALITY_VALUE_COMPARATOR));
🌐
ConcretePage
concretepage.com › java › java-8 › java-comparator-thencomparing
Java Comparator.thenComparing - ConcretePage.com
package com.concretepage; import java.util.Collections; import java.util.Comparator; import java.util.List; public class ThenComparingDemo { public static void main(String[] args) { List<Student> list = Student.getStudentList(); System.out.println("--------Example-1---------"); Comparator<Student> compByStdName = Comparator.comparing(Student::getName); Comparator<Student> schoolComparator1 = Comparator.comparing(Student::getAge) //sort by student age .thenComparing(compByStdName); //then sort by student name Collections.sort(list, schoolComparator1); list.forEach(s->System.out.println(s.getName() + "-" + s.getAge())); System.out.println("--------Example-2---------"); Comparator<Student> schoolComparator2 = Comparator.comparing(Student::getSchool) //sort by school natural ordering i.e.
🌐
Oracle
docs.oracle.com › en › java › javase › 17 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 17 & JDK 17)
January 20, 2026 - API Note: For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code, Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER); Parameters: ...
Find elsewhere
🌐
Medium
medium.com › @AlexanderObregon › javas-comparator-comparing-method-explained-342361288af6
Java’s Comparator.comparing() Method Explained | Medium
October 2, 2024 - We'll go through a detailed example using a class Product, demonstrating how sorting can be done on various fields, as well as combining sorting conditions to meet specific requirements. To illustrate the power of Comparator.comparing(), we’ll work with a Product class that has three main attributes: a product name, price, and a manufacture date. Here’s how the class is structured: import java.time.LocalDate; public class Product { private String name; private double price; private LocalDate manufactureDate; public Product(String name, double price, LocalDate manufactureDate) { this.name =
🌐
Readthedocs
java-8-tips.readthedocs.io › en › stable › comparator.html
7. Comparator — Java 8 tips 1.0 documentation
Below code snippet shows example of sorting employee objects based on employee’s salary and then uses name if two salaries are equal. List<Employee> employees = Application.getEmployees(); employees.stream() .sorted(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName)) ...
🌐
Oracle
docs.oracle.com › en › java › javase › 21 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 21 & JDK 21)
January 20, 2026 - API Note: For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code, Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER); Parameters: ...
🌐
Oracle
docs.oracle.com › en › java › javase › 11 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 11 & JDK 11 )
January 20, 2026 - default <U extends Comparable<? super U>> Comparator<T> thenComparing​(Function<? super T,​? extends U> keyExtractor)
Top answer
1 of 4
159

First, all the examples you say cause errors compile fine with the reference implementation (javac from JDK 8.) They also work fine in IntelliJ, so its quite possible the errors you're seeing are Eclipse-specific.

Your underlying question seems to be: "why does it stop working when I start chaining." The reason is, while lambda expressions and generic method invocations are poly expressions (their type is context-sensitive) when they appear as method parameters, when they appear instead as method receiver expressions, they are not.

When you say

Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));

there is enough type information to solve for both the type argument of comparing() and the argument type p1. The comparing() call gets its target type from the signature of Collections.sort, so it is known comparing() must return a Comparator<Song>, and therefore p1 must be Song.

But when you start chaining:

Collections.sort(playlist1,
                 comparing(p1 -> p1.getTitle())
                     .thenComparing(p1 -> p1.getDuration())
                     .thenComparing(p1 -> p1.getArtist()));

now we've got a problem. We know that the compound expression comparing(...).thenComparing(...) has a target type of Comparator<Song>, but because the receiver expression for the chain, comparing(p -> p.getTitle()), is a generic method call, and we can't infer its type parameters from its other arguments, we're kind of out of luck. Since we don't know the type of this expression, we don't know that it has a thenComparing method, etc.

There are several ways to fix this, all of which involve injecting more type information so that the initial object in the chain can be properly typed. Here they are, in rough order of decreasing desirability and increasing intrusiveness:

  • Use an exact method reference (one with no overloads), like Song::getTitle. This then gives enough type information to infer the type variables for the comparing() call, and therefore give it a type, and therefore continue down the chain.
  • Use an explicit lambda (as you did in your example).
  • Provide a type witness for the comparing() call: Comparator.<Song, String>comparing(...).
  • Provide an explicit target type with a cast, by casting the receiver expression to Comparator<Song>.
2 of 4
29

The problem is type inferencing. Without adding a (Song s) to the first comparison, comparator.comparing doesn't know the type of the input so it defaults to Object.

You can fix this problem 1 of 3 ways:

  1. Use the new Java 8 method reference syntax

     Collections.sort(playlist,
                Comparator.comparing(Song::getTitle)
                .thenComparing(Song::getDuration)
                .thenComparing(Song::getArtist)
                );
    
  2. Pull out each comparison step into a local reference

      Comparator<Song> byName = (s1, s2) -> s1.getArtist().compareTo(s2.getArtist());
    
      Comparator<Song> byDuration = (s1, s2) -> Integer.compare(s1.getDuration(), s2.getDuration());
    
        Collections.sort(playlist,
                byName
                .thenComparing(byDuration)
                );
    

    EDIT

  3. Forcing the type returned by the Comparator (note you need both the input type and the comparison key type)

    sort(
      Comparator.<Song, String>comparing((s) -> s.getTitle())
                .thenComparing(p1 -> p1.getDuration())
                .thenComparing(p1 -> p1.getArtist())
                );
    

I think the "last" thenComparing syntax error is misleading you. It's actually a type problem with the whole chain, it's just the compiler only marking the end of the chain as a syntax error because that's when the final return type doesn't match I guess.

I'm not sure why List is doing a better inferencing job than Collection since it should do the same capture type but apparently not.

🌐
Oracle
docs.oracle.com › javase › 10 › docs › api › java › util › Comparator.html
Comparator (Java SE 10 & JDK 10 )
default <U extends Comparable<? super U>> Comparator<T> thenComparing​(Function<? super T,? extends U> keyExtractor)
🌐
Oracle
docs.oracle.com › en › java › javase › 18 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 18 & JDK 18)
August 18, 2022 - API Note: For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code, Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER); Parameters: ...
Top answer
1 of 4
14

is Comparator.comparing() used for converting a single argument lambda expression to a double argument?

Yes, you can sort of think of it like that.

When sorting things, you are supposed to specify "given two things a and b, which of them is greater, or are they equal?" using a Comparator<T>. The a and b is why it has 2 lambda parameters, and you return an integer indicating your answer to that question.

However, a much more convenient way to do this is to specify "given a thing x, what part of x do you want to sort by?". And that is what you can do with the keyExtractor argument of Comparator.comparing.

Compare:

/*
given two people, a and b, the comparison result between a and b is the 
comparison result between a's name and b's name
*/
Comparator<Person> personNameComparator = 
    (a, b) -> a.getName().compareTo(b.getName());

/*
given a person x, compare their name
*/
Comparator<Person> personNameComparator = 
    Comparator.comparing(x -> x.getName()); // or Person::getName

The latter is clearly much more concise and intuitive. We tend to think about what things to sort by, rather than how exactly to compare two things, and the exact number to return depending on the comparison result.

As for the declaration for comparing:

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)

The <T, U extends Comparable<? super U>> part first declares two generic type parameters - T is what the comparator compares (Person in the above case), and U is the type that you are actually comparing (String in the above case), hence it extends Comparable.

keyExtractor is the parameter you pass in, such as x -> x.getName(), that should answer the question of "when given a T, what is a U that you want to compare by?".

If you are confused by the ? super and ? extends, read What is PECS?.

If you haven't realised already, the implementation of comparing basically boils down to:

return (a, b) -> keyExtractor.apply(a).compareTo(keyExtractor.apply(b));
2 of 4
7

Comparator#compare(T o1, T o2) Compare two objects and returns an integer value based on this criteria:

  • A negative value if o1 < o2
  • A positive value if o1 > o2
  • Zero if they are equal.

Comparator.comparing(Function<? super T, ? extends U> key) returns a Comparator<T> that compares by that sort key.

The main difference is that compare method provides a single point of comparison, whereas comparing chained to other functions to provide multiple points of comparison.

Suppose you have a class Person

public class Person implements Comparable<Person> {
    private String firstName;
    private String lastName;
    private int age;
    // rest of class omitted
}

if you compare two Person instances p1 vs p2 using compare(p1, p2), the comparison will be executed and the two objects will be sorted based on some natural ordering prescribed by the class. In contrast, if you want to compare the same two instances using comparing(), the comparison will be executed based on whichever criteria you choose to compare based on some attribute of the class. For example: Comparator.comparing(Person::getFirstName).

Because comparing returns a Comparator rather than a value, as I stated before, you can chain multiple comparisons. For instance: Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName);

As for the meaning of the return type <T, U extends Comparable<? super U>> Comparator<T>, you can find the explanation here.

I want to add that, classes must be comparable in order for compare(T o1, T o2) to work. String objects are comparable because they implement this interface. That said, if a class is not Comparable, you can still use comparing method because as I stated, you get to choose which attribute of the class you would like to use for the comparison and those attributes are likely to be comparable (i.e. String in the case of person's name or age in the above example).

🌐
GeeksforGeeks
geeksforgeeks.org › java › java-comparator-interface
Java Comparator Interface - GeeksforGeeks
Java 8 introduced a more simple way to write comparators using lambda expressions. We can use the method mentioned below for achieving same result: students.sort( Comparator.comparing(Student::getName) .thenComparing(Student::getAge) ); Java ·
Published   February 2, 2026
🌐
Oracle
docs.oracle.com › en › java › javase › 23 › docs › api › java.base › java › util › Comparator.html
Comparator (Java SE 23 & JDK 23)
October 17, 2024 - API Note: For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code, Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER); Parameters: ...