Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to sort a list by multiple fields

Short answer

thenComparing() adds the next field — it is compared only when the previous ones are equal:

record Employee(String department, String name) {}
 
void main() {
    var staff = new ArrayList<>(List.of(
            new Employee("Sales", "Oleg"),
            new Employee("IT", "Boris"),
            new Employee("Sales", "Anna"),
            new Employee("IT", "Anna")));
 
    staff.sort(Comparator.comparing(Employee::department).thenComparing(Employee::name));
    staff.forEach(e -> IO.println(e.department() + " " + e.name()));
}

Output:

IT Anna
IT Boris
Sales Anna
Sales Oleg

Different orders

To sort one field in descending order, pass a separate comparator to thenComparing():

record Player(String team, int score) {}
 
void main() {
    var players = new ArrayList<>(List.of(
            new Player("Red", 10), new Player("Blue", 30), new Player("Red", 25)));
 
    players.sort(Comparator.comparing(Player::team)
            .thenComparing(Player::score, Comparator.reverseOrder()));
    IO.println(players);
}

Output:

[Player[team=Blue, score=30], Player[team=Red, score=25], Player[team=Red, score=10]]

Null values

Comparator.nullsLast() puts elements with null at the end instead of throwing:

record Task(String title, Integer priority) {}
 
void main() {
    var tasks = new ArrayList<>(List.of(new Task("b", null), new Task("a", 2), new Task("c", 1)));
 
    tasks.sort(Comparator.comparing(Task::priority, Comparator.nullsLast(Comparator.naturalOrder())));
    IO.println(tasks);
}

Output:

[Task[title=c, priority=1], Task[title=a, priority=2], Task[title=b, priority=null]]

Common mistake

Putting reversed() at the end of the chain. It reverses the whole comparator, not only the last field:

record Player(String team, int score) {}
 
void main() {
    var players = new ArrayList<>(List.of(
            new Player("Red", 10), new Player("Blue", 30), new Player("Red", 25)));
 
    players.sort(Comparator.comparing(Player::team).thenComparing(Player::score).reversed());
    IO.println(players);
}

Output:

[Player[team=Red, score=25], Player[team=Red, score=10], Player[team=Blue, score=30]]

The teams got sorted in descending order too. Specify the order for the particular field, as in the example above.

Functions used in this recipe

Thanks for reading!

Donate to the site — we work hard for you.