11

This is my list:

Name: Ben     || Age: 5 || Group: 1
Name: Andy    || Age: 6 || Group: 2
Name: Charlie || Age: 6 || Group: 2
Name: Ben     || Age: 5 || Group: 1
Name: Andy    || Age: 5 || Group: 2
Name: Charlie || Age: 5 || Group: 1

I want to sort the list by Group, if Group is equal then by Age, and if Age is equal then by Name. But so far I can only sort by one attribute, using Lambda Expressions:

list.sort((Object o1, Object o2) -> o1.getGroup().compareTo(o2.getGroup()));

If I try

o1.getGroup().compareTo(o2.getGroup()) && o1.getAge().compareTo(o2.getAge())

it's turned out error...

Andreas
  • 154,647
  • 11
  • 152
  • 247

2 Answers2

19

Change lambda expression to lambda {block}, and you don't have to specify the parameter types:

list.sort((o1, o2) -> {
    int cmp = o1.getGroup().compareTo(o2.getGroup());
    if (cmp == 0)
        cmp = Integer.compare(o1.getAge(), o2.getAge());
    if (cmp == 0)
        cmp = o1.getName().compareTo(o2.getName());
    return cmp;
});
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 1
    You are the saviour of my day :) Thanks –  Oct 25 '15 at 06:28
  • 5
    @HuyVo The right way to thank somebody here is by upvoting their answers. – Edwin Dalorzo Oct 25 '15 at 07:02
  • How to do this with raw arrays (not Lists)? This syntax does not seem to work when sorting an `int[] arr` with: `Arrays.sort(arr, );` – PlsWork Apr 07 '20 at 15:25
  • @AnnaVopureta There are no sorting methods for sorting primitive arrays by custom order. The Java runtime library only has methods for sorting primitive arrays by their natural order. See: [How to sort an array of ints using a custom comparator?](https://stackoverflow.com/q/3699141/5221149) – Andreas Apr 07 '20 at 20:42
16

You can use the static method Comparator.comparing to create a comparator based on a function that returns a comparable value. Such a comparator can be chained with others.

Assuming your type is called Person, you would have:

Comparator<Person> c = Comparator
        .comparing(p -> p.getGroup())
        .thenComparing(p -> p.getAge())
        .thenComparing(p -> p.getName())

If any of the getters return a primitive type, you have to use - for example - comparingInt and thenComparingInt, respectively. You can also use method references:

Comparator<Person> c = Comparator
        .comparing(Person::getGroup)
        .thenComparing(Person::getAge)
        .thenComparing(Person::getName)

But ... if your class has a natural ordering according to these values, you better let it implement the interface Comparable and write the compare logic in there:

class Person implements Comparable<Person> {
    ...
    @Override
    public int compareTo(Person other) {
        int compare = Integer.compare(getGroup(), other.getGroup());
        if (compare == 0) {
            compare = Integer.compare(getAge(), other.getAge());
        }
        if (compare == 0) {
            compare = getName.compareTo(other.getName());
        }
        return compare;
    }
}

This code snippet can also be used in a lambda expression:

list.sort((o1, o2) -> {
    int compare = Integer.compare(o1.getGroup(), o2.getGroup());
    if (compare == 0) {
        compare = Integer.compare(o1.getAge(), o2.getAge());
    }
    if (compare == 0) {
        compare = o1.getName.compareTo(o2.getName());
    }
    return compare;
});
Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66
  • 1
    Use `Integer.compare()` instead of subtraction, to prevent bad results on very large values. – Andreas Oct 25 '15 at 06:30
  • 1
    Your first example doesn’t work. The compiler has no chance to deduce that `p` is a `Person`… – Holger Oct 25 '15 at 08:51