Java compare List of objects field by field

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 dont have a natural ordering. This classs 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 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[] {
@Override
public int compare[Person p1, Person p2] {
return p1.getAge[] - p2.getAge[];
}
}];
System.out.println[persons];
}
}

DownloadRun Code

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[] {
@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 byAge = Comparator.comparing[Person::getAge];
Collections.sort[persons, byAge];


The above code will sort the persons 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 persons list first by age and then by name, as shown below. Now for persons having the same age, the ordering is decided by the persons name.

1
2
3
4
5
6
7
8
9
10
Collections.sort[persons, new Comparator[] {
@Override
public int compare[Person p1, Person p2]
{
if [p1.getAge[] != p2.getAge[]] {
return p1.getAge[] - p2.getAge[];
}
return p1.getName[].compareTo[p2.getName[]];
}
}];

DownloadRun Code


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 byAge = Comparator.comparing[Person::getAge];
Comparator byName = Comparator.comparing[Person::getName];
Collections.sort[persons, byAge.thenComparing[byName]];

DownloadRun Code


3. We can also use Guavas 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[] {
@Override
public int compare[Person p1, Person p2]
{
return ComparisonChain.start[]
.compare[p1.getAge[], p2.getAge[]]
.compare[p1.getName[], p2.getName[]]
.result[];
}
}];

Download Code


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[] {
@Override
public int compare[Person p1, Person p2]
{
return new CompareToBuilder[]
.append[p1.getAge[], p2.getAge[]]
.append[p1.getName[], p2.getName[]]
.toComparison[];
}
}];

Download Code


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 classs 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
{
@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 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];
}
}

DownloadRun Code

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 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];
}
}

DownloadRun Code

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. Heres 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 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];
}
}

DownloadRun Code

Output:

[{name='Joe', age=10}, {name='John', age=15}, {name='Dan', age=20}, {name='Will', age=20}, {name='Sam', age=25}]

Thats all about sorting a list of objects using Comparator in Java.


Continue reading:

Sort a list of objects using Comparable in Java

Video liên quan

Chủ Đề