You must have been came across the situation where you have to sort a collection of objects by a certain rule. And if you are working on a domain where people are your main entities, you will encounter it more. There must be many ways to it, which ranges from sorting on client UI using javascript to sorting on server side using complex algorithm, and sometimes in database too.
If you do not have millions of records to play with at a time then for sorting i will recommend you to consider comparable or comparator interface.
Sections in this post:
- Defining Employee class
- Implementing Comparable interface and Testing our code
- What if we want to sort on different fields??
- Adding different Comparator interface implementations
- Testing our code
- Understanding uses in other core collection classes
- Caution words
Defining Employee class
Our employee class is plain old class with four fields: id, firstName, lastName and age. I have chosen these fields purposefully. We will see usage later in this post.
01.
package
corejava.compare;
02.
03.
public
class
Employee {
04.
private
int
id = -
1
;
05.
private
String firstName =
null
;
06.
private
String lastName =
null
;
07.
private
int
age = -
1
;
08.
09.
public
Employee(
int
id, String fName, String lName,
int
age) {
10.
this
.id = id;
11.
this
.firstName = fName;
12.
this
.lastName = lName;
13.
this
.age = age;
14.
}
15.
16.
@Override
17.
public
String toString() {
18.
return
"Employee : "
+ id +
" - "
+ firstName +
" - "
+ lastName
19.
+
" - "
+ age +
"n"
;
20.
}
21.
22.
public
int
getId() {
23.
return
id;
24.
}
25.
26.
public
void
setId(
int
id) {
27.
this
.id = id;
28.
}
29.
30.
// Other accessor methods here
31.
}
Implementing Comparable interface and Testing our code
Comparable interface provides one method compare(Object o) to implement in any class so that two instances of that class can be compared. Signature of method is:
1.
public
int
compareTo(T o);
Here, out of two instances to compare, one is instance itself on which method will be invoked, and other is passed as parameter o.
Lets see how our Employee class will look after implementing Comparable interface.
01.
package
corejava.compare;
02.
03.
public
class
Employee
implements
Comparable<employee> {
04.
private
int
id = -
1
;
05.
private
String firstName =
null
;
06.
private
String lastName =
null
;
07.
private
int
age = -
1
;
08.
09.
public
Employee(
int
id, String fName, String lName,
int
age) {
10.
this
.id = id;
11.
this
.firstName = fName;
12.
this
.lastName = lName;
13.
this
.age = age;
14.
}
15.
16.
@Override
17.
public
int
compareTo(Employee o) {
18.
return
this
.id - o.id;
19.
}
20.
21.
@Override
22.
public
String toString() {
23.
return
"Employee : "
+ id +
" - "
+ firstName +
" - "
+ lastName
24.
+
" - "
+ age +
"n"
;
25.
}
26.
27.
public
int
getId() {
28.
return
id;
29.
}
30.
31.
public
void
setId(
int
id) {
32.
this
.id = id;
33.
}
34.
35.
// Other accessor methods
36.
}
Default way to sort a list of employees, in our case, is by their id. Whatever, your default sorting order is, use in compare() method.
In implemented compare() method, we have simply returned the difference in employee ids of two instances. Two equal employee ids will return zero, indicating same object.
Lets test our compare() method.
01.
package
corejava.compare;
02.
03.
import
java.util.ArrayList;
04.
import
java.util.Collections;
05.
import
java.util.List;
06.
07.
public
class
TestSorting {
08.
public
static
void
main(String[] args) {
09.
Employee e1 =
new
Employee(
1
,
"aTestName"
,
"dLastName"
,
34
);
10.
Employee e2 =
new
Employee(
2
,
"nTestName"
,
"pLastName"
,
30
);
11.
Employee e3 =
new
Employee(
3
,
"kTestName"
,
"sLastName"
,
31
);
12.
Employee e4 =
new
Employee(
4
,
"dTestName"
,
"zLastName"
,
25
);
13.
14.
List</employee><employee> employees =
new
ArrayList</employee><employee>();
15.
employees.add(e2);
16.
employees.add(e3);
17.
employees.add(e1);
18.
employees.add(e4);
19.
20.
// UnSorted List
21.
System.out.println(employees);
22.
23.
Collections.sort(employees);
24.
// Default Sorting by employee id
25.
System.out.println(employees);
26.
}
27.
}
In above program, first print statement prints an unsorted list of employees and in second print statement, employees are sorted by their employee id.
What if we want to sort on different fields??
So, now we can sort a collection of employees by their id. Now lets consider a case where we want to sort employees list based on some user input which is essentially sorting field i.e. sometime he want to sort by first name, sometimes by even age also.
This can be achieved by jquery plugins easily, but what if browser has disables the java script. You will have to sort the list on server side only to not break the application functionality.
Here comes the Comparators to rescue you. A comparator can be used to sort a collection of instances on some particular basis. To sort of different fields, we need multiple comparators.
Adding different Comparator interface implementations
As discussed in last section, we need to have multiple implementations for different sorting cases. Lets write them one by one:
First name sorter
01.
package
corejava.compare;
02.
03.
import
java.util.Comparator;
04.
05.
public
class
FirstNameSorter
implements
Comparator</employee><employee>{
06.
07.
@Override
08.
public
int
compare(Employee o1, Employee o2) {
09.
return
o1.getFirstName().compareTo(o2.getFirstName());
10.
}
11.
}
Last name sorter
01.
package
corejava.compare;
02.
03.
import
java.util.Comparator;
04.
05.
public
class
LastNameSorter
implements
Comparator</employee><employee> {
06.
07.
@Override
08.
public
int
compare(Employee o1, Employee o2) {
09.
return
o1.getLastName().compareTo(o2.getLastName());
10.
}
11.
12.
}
Age sorter
01.
package
corejava.compare;
02.
03.
import
java.util.Comparator;
04.
05.
public
class
AgeSorter
implements
Comparator</employee><employee> {
06.
@Override
07.
public
int
compare(Employee o1, Employee o2) {
08.
return
o1.getAge() - o2.getAge();
09.
}
10.
}
Testing our code
So theoretically, we should be able to sort of any field at our wish with minimum code. Lets see if we really are:
01.
package
corejava.compare;
02.
03.
import
java.util.ArrayList;
04.
import
java.util.Collections;
05.
import
java.util.List;
06.
07.
public
class
TestSorting {
08.
public
static
void
main(String[] args) {
09.
Employee e1 =
new
Employee(
1
,
"aTestName"
,
"dLastName"
,
34
);
10.
Employee e2 =
new
Employee(
2
,
"nTestName"
,
"pLastName"
,
30
);
11.
Employee e3 =
new
Employee(
3
,
"kTestName"
,
"sLastName"
,
31
);
12.
Employee e4 =
new
Employee(
4
,
"dTestName"
,
"zLastName"
,
25
);
13.
14.
List</employee><employee> employees =
new
ArrayList</employee><employee>();
15.
employees.add(e2);
16.
employees.add(e3);
17.
employees.add(e1);
18.
employees.add(e4);
19.
20.
// UnSorted List
21.
System.out.println(employees);
22.
23.
Collections.sort(employees);
24.
// Default Sorting by employee id
25.
System.out.println(employees);
26.
27.
Collections.sort(employees,
new
FirstNameSorter());
28.
// Sorted by firstName
29.
System.out.println(employees);
30.
31.
Collections.sort(employees,
new
LastNameSorter());
32.
// Sorted by lastName
33.
System.out.println(employees);
34.
35.
Collections.sort(employees,
new
AgeSorter());
36.
// Sorted by age
37.
System.out.println(employees);
38.
}
39.
}
40.
41.
Output:
42.
43.
Employee :
2
- nTestName - pLastName -
30
44.
, Employee :
3
- kTestName - sLastName -
31
45.
, Employee :
1
- aTestName - dLastName -
34
46.
, Employee :
4
- dTestName - zLastName -
25
47.
]
48.
[Employee :
1
- aTestName - dLastName -
34
49.
, Employee :
2
- nTestName - pLastName -
30
50.
, Employee :
3
- kTestName - sLastName -
31
51.
, Employee :
4
- dTestName - zLastName -
25
52.
]
53.
[Employee :
1
- aTestName - dLastName -
34
54.
, Employee :
4
- dTestName - zLastName -
25
55.
, Employee :
3
- kTestName - sLastName -
31
56.
, Employee :
2
- nTestName - pLastName -
30
57.
]
58.
[Employee :
1
- aTestName - dLastName -
34
59.
, Employee :
2
- nTestName - pLastName -
30
60.
, Employee :
3
- kTestName - sLastName -
31
61.
, Employee :
4
- dTestName - zLastName -
25
62.
]
63.
[Employee :
4
- dTestName - zLastName -
25
64.
, Employee :
2
- nTestName - pLastName -
30
65.
, Employee :
3
- kTestName - sLastName -
31
66.
, Employee :
1
- aTestName - dLastName -
34
67.
]
Above class’s output shows clearly, now we are able to sort the collection of employees on any field using appropriate comparator implementation.
Understanding uses in other core collection classes
Its important to understand the importance of both interfaces in code collection APIs which have implicit behavior or sorting the elements. Such APIs are for example SortedMap or SortedSet.
These collections store elements in sorted order. To determining the sorting, they also use compare() method. Always remember if you Employee class does not implement Comparable interface and you are not using Comparator also, then adding elements in Sorted collections will give you error:
1.
Exception in thread
"main"
java.lang.ClassCastException: corejava.compare.Employee cannot be cast to java.lang.Comparable
2.
at java.util.TreeMap.put(Unknown Source)
3.
at java.util.TreeSet.add(Unknown Source)
4.
at corejava.compare.SortedSetTest.main(SortedSetTest.java:
17
)
So,its necessary to use either of both interfaces to able to store instances in sorted collections.
If you are not using comparator implementation, then Employee class’s compare() method will be used for sorting:
01.
package
corejava.compare;
02.
03.
import
java.util.SortedSet;
04.
import
java.util.TreeSet;
05.
06.
public
class
SortedSetTest {
07.
public
static
void
main(String[] args) {
08.
SortedSet</employee><employee> set =
new
TreeSet</employee><employee>();
09.
10.
Employee e1 =
new
Employee(
1
,
"aTestName"
,
"dLastName"
,
34
);
11.
Employee e2 =
new
Employee(
2
,
"nTestName"
,
"pLastName"
,
30
);
12.
Employee e3 =
new
Employee(
3
,
"kTestName"
,
"sLastName"
,
31
);
13.
Employee e4 =
new
Employee(
4
,
"dTestName"
,
"zLastName"
,
25
);
14.
15.
set.add(e2);
16.
set.add(e3);
17.
set.add(e1);
18.
set.add(e4);
19.
20.
System.out.println(set);
21.
}
22.
}
If you want to add some other sorting behavior using comparator implementation, you can pass the comparator to constructor of sorted collection. e.g.
01.
package
corejava.compare;
02.
03.
import
java.util.SortedSet;
04.
import
java.util.TreeSet;
05.
06.
public
class
SortedSetTest {
07.
public
static
void
main(String[] args) {
08.
SortedSet</employee><employee> set =
new
TreeSet</employee><employee>(
new
FirstNameSorter());
09.
10.
Employee e1 =
new
Employee(
1
,
"aTestName"
,
"dLastName"
,
34
);
11.
Employee e2 =
new
Employee(
2
,
"nTestName"
,
"pLastName"
,
30
);
12.
Employee e3 =
new
Employee(
3
,
"kTestName"
,
"sLastName"
,
31
);
13.
Employee e4 =
new
Employee(
4
,
"dTestName"
,
"zLastName"
,
25
);
14.
15.
set.add(e2);
16.
set.add(e3);
17.
set.add(e1);
18.
set.add(e4);
19.
20.
System.out.println(set);
21.
}
22.
}
In above code, comparator will take precedence and will become default sort order. So easy it is, right??
Now a bit of caution
If you have overridden equals() method in your Employee class, always remember to honor its behavior. I mean, if two objects are equal using equals() method that compare method should return ’0′ when comparing two such equal methods. It is required not to be surprised by abnormal behavior and not able to add element in sorted collections.
Hope i was clear enough in showing their usage. If you still have left with some doubts, leave a comment. I will be happy to resolve.
Happy Learning !!
转自:http://howtodoinjava.com/2012/10/21/when-to-use-comparable-and-comparator-interfaces-in-java/