
Making Java Objects Comparable
Pages: 1, 2
Using the java.util.Comparator
Class
Implementing the Comparable
interface enables you to define one
way to compare instances of your class. However, objects are sometimes
comparable in many ways. For example, two Person
objects may need
to be compared by age or by last/first name. In cases like this, create a
Comparator
that defines how to compare two objects. To make
objects comparable in two ways, then you need two comparators.
To create a comparator, write a class that implements the
java.util.Comparator
interface--the compare
method.
This method has the following signature:
public int compare(Object o1, Object o2)
The compare
method returns zero if o1
and
o2
are equal, a negative integer if o1
is less than
o2
, and a positive integer if o1
is greater than
o2
. Just as in the compareTo
method of
Comparable
, you define what makes an object equal or less/greater
than another object.
As an example, let's write two comparators for the Person
class. This example consists of four classes, all of which reside in the
comparable.ex02
package. The Person
class is similar
to the one in the previous example, and is reprinted in Listing 6 for reading
convenience. Listings 7 and 8 present two comparators of Person
objects (by last name and by first name), and Listing 9 offers the class that
instantiates the Person
class and the two comparators.
Listing 6: The comparable.ex02.Person
Class
package comparable.ex02;
class Person implements Comparable {
private String firstName;
private String lastName;
private int age;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int compareTo(Object anotherPerson) throws ClassCastException {
if (!(anotherPerson instanceof Person))
throw new ClassCastException("A Person object expected.");
int anotherPersonAge = ((Person) anotherPerson).getAge();
return this.age - anotherPersonAge;
}
}
Listing 7: The comparable.ex02.LastNameComparator
Class
package comparable.ex02;
import java.util.Comparator;
public class LastNameComparator implements Comparator {
public int compare(Object person, Object anotherPerson) {
String lastName1 = ((Person) person).getLastName().toUpperCase();
String firstName1 = ((Person) person).getFirstName().toUpperCase();
String lastName2 = ((Person) anotherPerson).getLastName().toUpperCase();
String firstName2 = ((Person) anotherPerson).getFirstName().toUpperCase();
if (!(lastName1.equals(lastName2)))
return lastName1.compareTo(lastName2);
else
return firstName1.compareTo(firstName2);
}
}
Listing 8: The comparable.ex02.FirstNameComparator
Class
package comparable.ex02;
import java.util.Comparator;
public class FirstNameComparator implements Comparator {
public int compare(Object person, Object anotherPerson) {
String lastName1 = ((Person) person).getLastName().toUpperCase();
String firstName1 = ((Person) person).getFirstName().toUpperCase();
String lastName2 = ((Person) anotherPerson).getLastName().toUpperCase();
String firstName2 = ((Person) anotherPerson).getFirstName().toUpperCase();
if (!(firstName1.equals(firstName2)))
return firstName1.compareTo(firstName2);
else
return lastName1.compareTo(lastName2);
}
}
Listing 9: The comparable.ex02.Testing
Class
package comparable.ex02;
import java.util.Arrays;
import java.util.ArrayList;
public class Testing {
public static void main(String[] args) {
Person[] persons = new Person[4];
persons[0] = new Person();
persons[0].setFirstName("Elvis");
persons[0].setLastName("Goodyear");
persons[0].setAge(56);
persons[1] = new Person();
persons[1].setFirstName("Stanley");
persons[1].setLastName("Clark");
persons[1].setAge(8);
persons[2] = new Person();
persons[2].setFirstName("Jane");
persons[2].setLastName("Graff");
persons[2].setAge(16);
persons[3] = new Person();
persons[3].setFirstName("Nancy");
persons[3].setLastName("Goodyear");
persons[3].setAge(69);
System.out.println("Natural Order");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons, new LastNameComparator());
System.out.println();
System.out.println("Sorted by last name");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons, new FirstNameComparator());
System.out.println();
System.out.println("Sorted by first name");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons);
System.out.println();
System.out.println("Sorted by age");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
}
}
If you run the comparable.ex02.Testing
class, you can see the
following result:
Natural Order
Goodyear, Elvis. Age:56
Clark, Stanley. Age:8
Graff, Jane. Age:16
Goodyear, Nancy. Age:69
Sorted by last name
Clark, Stanley. Age:8
Goodyear, Elvis. Age:56
Goodyear, Nancy. Age:69
Graff, Jane. Age:16
Sorted by first name
Goodyear, Elvis. Age:56
Graff, Jane. Age:16
Goodyear, Nancy. Age:69
Clark, Stanley. Age:8
Sorted by age
Clark, Stanley. Age:8
Graff, Jane. Age:16
Goodyear, Elvis. Age:56
Goodyear, Nancy. Age:69
Integrating Comparators in Comparable Classes
The previous example with the Comparator
interface works fine.
However, the drawback is that it requires multiple classes. This means more
maintenance work for the users of your comparable class. The next example shows
how to integrate the comparators inside of the comparable class by making the
comparators anonymous classes.
This example has two classes: comparable.ex03.Person
(Listing 10) and comparable.ex03.Testing
(Listing 11). Note the two
anonymous inner classes at the end of the Person
class, and notice
also in the Testing
class how comparison is conducted.
Listing 10: The comparable.ex03.Person
Class
package comparable.ex03;
import java.util.Comparator;
public class Person implements Comparable {
private String firstName;
private String lastName;
private int age;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int compareTo(Object anotherPerson) throws ClassCastException {
if (!(anotherPerson instanceof Person))
throw new ClassCastException("A Person object expected.");
int anotherPersonAge = ((Person) anotherPerson).getAge();
return this.age - anotherPersonAge;
}
public static Comparator LastNameComparator = new Comparator() {
public int compare(Object person, Object anotherPerson) {
String lastName1 = ((Person) person).getLastName().toUpperCase();
String firstName1 = ((Person) person).getFirstName().toUpperCase();
String lastName2 = ((Person) anotherPerson).getLastName().toUpperCase();
String firstName2 = ((Person) anotherPerson).getFirstName().toUpperCase();
if (!(lastName1.equals(lastName2)))
return lastName1.compareTo(lastName2);
else
return firstName1.compareTo(firstName2);
}
};
public static Comparator FirstNameComparator = new Comparator() {
public int compare(Object person, Object anotherPerson) {
String lastName1 = ((Person) person).getLastName().toUpperCase();
String firstName1 = ((Person) person).getFirstName().toUpperCase();
String lastName2 = ((Person) anotherPerson).getLastName().toUpperCase();
String firstName2 = ((Person) anotherPerson).getFirstName().toUpperCase();
if (!(firstName1.equals(firstName2)))
return firstName1.compareTo(firstName2);
else
return lastName1.compareTo(lastName2);
}
};
}
Listing 11: The comparable.ex03.Testing
Class
package comparable.ex03;
import java.util.Arrays;
import java.util.ArrayList;
public class Testing {
public static void main(String[] args) {
Person[] persons = new Person[4];
persons[0] = new Person();
persons[0].setFirstName("Elvis");
persons[0].setLastName("Goodyear");
persons[0].setAge(56);
persons[1] = new Person();
persons[1].setFirstName("Stanley");
persons[1].setLastName("Clark");
persons[1].setAge(8);
persons[2] = new Person();
persons[2].setFirstName("Jane");
persons[2].setLastName("Graff");
persons[2].setAge(16);
persons[3] = new Person();
persons[3].setFirstName("Nancy");
persons[3].setLastName("Goodyear");
persons[3].setAge(69);
System.out.println("Natural Order");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons, Person.LastNameComparator);
System.out.println();
System.out.println("Sorted by last name");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons, Person.FirstNameComparator);
System.out.println();
System.out.println("Sorted by first name");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
Arrays.sort(persons);
System.out.println();
System.out.println("Sorted by age");
for (int i=0; i<4; i++) {
Person person = persons[i];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName + ", " + firstName + ". Age:" + age);
}
}
}
The result of the Testing
class is the same as the previous
example. However, note that the comparators are inside of the Person
class. To sort instances of the Person
class by last name, you
just need to use:
Arrays.sort(persons, Person.LastNameComparator);
To sort them by first name:
Arrays.sort(persons, Person.LastNameComparator);
Summary
This article has demonstrated how to make class instances comparable and
sortable. Example 1 shows that implementing the compareTo
method
of the java.lang.Comparable
interface is the easiest solution. To
compare instances in multiple ways, create comparators by implementing the
java.util.Comparator
class, as demonstrated in Example 2. For
better maintenance, you can embed the comparators as anonymous classes in your
comparable class, as shown in Example 3.
Budi Kurniawan is a senior J2EE architect and author.
Return to ONJava.com.
