Sort a list of objects using Comparator in Java
This post will discuss how to sort a list of objects using Comparator in Java.
A Comparator is a comparison function, which provides an ordering for collections of objects that don’t have a natural ordering. This class’s implementer needs to override the abstract method compare()
defined in java.util.Comparator
, which compares its two arguments for order. The value returned by the compare()
method decides the position of the first object relative to the second object.
- If
compare()
returns a negative integer, the first argument is less than the second. - If
compare()
returns a zero, the first argument is equal to the second. - If
compare()
returns a positive integer, the first argument is greater than the second.
There are several ways to implement Comparators in Java:
1. Pass Comparator as argument to sort()
method
Comparators, if passed to a sort method (such as Collections.sort
(and Arrays.sort
), allow precise control over the sort order. In the following example, we obtain a Comparator
that compares Person
objects by their age.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import java.util.*; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public int getAge() { return age; } } class Main { public static void main(String[] args) { List<Person> persons = new ArrayList<>(Arrays.asList( new Person("John", 15), new Person("Sam", 25), new Person("Will", 20), new Person("Dan", 20), new Person("Joe", 10) )); Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.getAge() - p2.getAge(); } }); System.out.println(persons); } } |
Output:
[{name='Joe', age=10}, {name='John', age=15}, {name='Will', age=20}, {name='Dan', age=20}, {name='Sam', age=25}]
Since Comparator
is a functional interface, it can be used as the assignment target for a lambda expression or method reference. Therefore,
1 2 3 4 5 6 |
Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.getAge() - p2.getAge(); } }); |
can be rewritten as:
1 |
Collections.sort(persons, (p1, p2) -> p1.getAge() - p2.getAge()); |
Java 8 introduced several enhancements to the Comparator
interface. Now Comparator has static methods like comparing()
, which can easily create Comparators to compare some specific values from objects. For example, to obtain a Comparator
that compares Person
objects by their age, we can do:
1 2 |
Comparator<Person> byAge = Comparator.comparing(Person::getAge); Collections.sort(persons, byAge); |
The above code will sort the person’s list only by the age
field. If two people have the same age, their relative ordering in the sorted list is not fixed. So, it is preferred to compare objects using multiple fields to avoid such cases.
How to compare objects against the multiple fields?
1. We can easily sort the person’s list first by age
and then by name
, as shown below. Now for persons having the same age, the ordering is decided by the person’s name.
1 2 3 4 5 6 7 8 9 10 |
Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { if (p1.getAge() != p2.getAge()) { return p1.getAge() - p2.getAge(); } return p1.getName().compareTo(p2.getName()); } }); |
Note that we have simply reduced the value of int
primitive type age
from each other while for the String object, built-in comparison method compareTo()
is used. Typically, this chain can continue further to include other properties as well.
2. We can also do this with lambda expressions by using the .thenComparing()
method, which effectively combines two comparisons into one:
1 2 3 4 |
Comparator<Person> byAge = Comparator.comparing(Person::getAge); Comparator<Person> byName = Comparator.comparing(Person::getName); Collections.sort(persons, byAge.thenComparing(byName)); |
3. We can also use Guava’s ComparisonChain for performing a chained comparison statement, as shown below:
1 2 3 4 5 6 7 8 9 10 |
Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return ComparisonChain.start() .compare(p1.getAge(), p2.getAge()) .compare(p1.getName(), p2.getName()) .result(); } }); |
The return value of the compare()
method will have the same sign as the first nonzero comparison result in the chain or will be zero if every comparison result was zero. Note that the ComparisonChain
stops calling its inputs compareTo
and compare
methods as soon as one of them returns a nonzero result.
4. We can also use the CompareToBuilder class of the Apache Commons Lang library to assist in implementing the Comparator.compare()
method. To use this class, write code as follows:
1 2 3 4 5 6 7 8 9 10 |
Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return new CompareToBuilder() .append(p1.getAge(), p2.getAge()) .append(p1.getName(), p2.getName()) .toComparison(); } }); |
Values are compared in the order they are appended to the builder. If any comparison returns a nonzero result, then that value will be returned by toComparison()
, and all subsequent comparisons are skipped.
2. Implement Comparator in a separate class
We can even implement Comparator
in a separate class and then pass that class’s instance to the sort()
method. This is demonstrated below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
import java.util.*; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public int getAge() { return age; } } class MyComparator implements Comparator<Person> { @Override public int compare(Person p1, Person p2) { if (p1.getAge() != p2.getAge()) { return p1.getAge() - p2.getAge(); } return p1.getName().compareTo(p2.getName()); } } class Main { public static void main(String[] args) { List<Person> persons = new ArrayList<>(Arrays.asList( new Person("John", 15), new Person("Sam", 25), new Person("Will", 20), new Person("Dan", 20), new Person("Joe", 10) )); Collections.sort(persons, new MyComparator()); System.out.println(persons); } } |
Output:
[{name='Joe', age=10}, {name='John', age=15}, {name='Dan', age=20}, {name='Will', age=20}, {name='Sam', age=25}]
3. Pass Comparator to List.sort()
method
Java 8 introduced several enhancements to the List
interface. Now List
has its own sorting method sort() which sorts the list according to the order induced by the specified Comparator
. This is demonstrated below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import java.util.*; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public int getAge() { return age; } } class Main { public static void main(String[] args) { List<Person> persons = new ArrayList<>(Arrays.asList( new Person("John", 15), new Person("Sam", 25), new Person("Will", 20), new Person("Dan", 20), new Person("Joe", 10) )); persons.sort(Comparator.comparing(Person::getAge) .thenComparing(Comparator.comparing(Person::getName))); System.out.println(persons); } } |
Output:
[{name='Joe', age=10}, {name='John', age=15}, {name='Dan', age=20}, {name='Will', age=20}, {name='Sam', age=25}]
4. Pass Comparator to Stream.sorted()
method
We can also pass our comparator to the sorted()
method of the Stream
class, which returns a stream consisting of the elements of this stream, sorted according to the provided Comparator
. Here’s a working example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public int getAge() { return age; } } class Main { public static void main(String[] args) { List<Person> persons = new ArrayList<>(Arrays.asList( new Person("John", 15), new Person("Sam", 25), new Person("Will", 20), new Person("Dan", 20), new Person("Joe", 10) )); persons = persons.stream() .sorted(Comparator.comparing(Person::getAge) .thenComparing(Comparator.comparing(Person::getName))) .collect(Collectors.toList()); System.out.println(persons); } } |
Output:
[{name='Joe', age=10}, {name='John', age=15}, {name='Dan', age=20}, {name='Will', age=20}, {name='Sam', age=25}]
That’s all about sorting a list of objects using Comparator in Java.
Continue reading:
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)