Posts Tagged Comparator

Generics and Comparison

A few years back, I wrote a post about how to use the Comparable and Comparator interfaces for comparing objects, “Two, If By Comparison“. While I was bringing the post over to this new site, I realized that it was written in the age long ago before Java generics. I decided to do a quick update to show how adding generics makes is a little easier.

Our first update will be the Comparable version of the Person class:

1
2
3
4
5
6
7
8
9
10
11
public class Person implements Comparable<Person> {
    private String id, firstName, lastName;
    private int age;

    // not showing all the getters and setters here...

    public int compareTo(Person person){
            // compare the last names using their compareTo methods
            return(lastName.compareTo(person.getLastName()));
    }
}

Note the Person parameter in the Comparable interface and the Person parameter in the compareTo() method. It gets rid of a few lines and makes the code a little more clear.

The Comparators become a bit more straight-forward as well:

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
public class LastNameComparator implements Comparator<Person> {

    public boolean equals(Object obj){
            // we're just going to say that any LastNameComparators are equal
            return(obj instanceof LastNameComparator);
    }

    public int compare(Person p1, Person p2){
            // compare their lastNames
            return(p1.getLastName().compareTo(p2.getLastName()));
    }
}

public class AgeComparator implements Comparator<Person> {

    public boolean equals(Object obj){
            // we're just going to say that any AgeComparators are equal
            return(obj instanceof AgeComparator);
    }

    public int compare(Person p1, Person p2){
            // compare their ages
            int result = 0; // defaults to equal
            if(p1.getAge() > p2.getAge()){
                    result = 1;
            } else if(p1.getAge() < p2.getAge()){
                    result = -1;
            }

            return(result);
    }
}

Okay, so it’s nothing really astounding or Earth-shattering, but I felt the need to give a little update.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , ,

Two, If By Comparison

There are two ways to compare objects, directly if they implement the java.lang.Comparable interface, and indirectly using an implementation of the java.util.Comparator interface.

java.lang.Comparable

The Comparable interface defines only a single method signature, the compareTo() method that takes as a parameter the object that the implementing object is being compared to. It returns a negative integer, zero, or a positive integer when the implementing object is less than, equal to, or greater than the specified object respectively.

The Comparable interface is best used when your object has a logical point of comparison, such as an order number, date stamp, or unique name and that point of comparison is fixed, meaning that the comparison is always performed on the same comparison point (e.g. always compared by the date stamp). This also assumes that you are the developer of the object or that you are able to extend it to allow for a Comparable implementation (if neither of these is the case, you should use the Comparator interface instead — see below).

First you need a class for your object.

1
2
3
4
5
6
7
8
9
10
11
12
public class Person {
    private String id, firstName, lastName;
    private int age;
    public String getId(){return(id);}
    public String getFirstName(){return(firstName);}
    public String getLastName(){return(lastName);}
    public int getAge(){return(age);}
    public void setId(String id){this.id = id;}
    public void setFirstName(String firstName){this.firstName = firstName;}
    public void setLastName(String lastName){this.lastName = lastName;}
    public void setAge(int age){this.age = age;}
}

Now, say you have a list of people (Person objects) and that you want to display them ordered by their last names. You can make the Person class implement Comparable and perform the comparison based on the lastName property. This comparison is very easy due to the fact that Strings implement Comparable themselves, as do many standard data object classes (Numbers, Dates, etc.). The comparable Person class is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person implements Comparable {
    private String id, firstName, lastName;
    private int age;

    public String getId(){return(id);}
    public String getFirstName(){return(firstName);}
    public String getLastName(){return(lastName);}
    public int getAge(){return(age);}
    public void setId(String id){this.id = id;}
    public void setFirstName(String firstName){this.firstName = firstName;}
    public void setLastName(String lastName){this.lastName = lastName;}
    public void setAge(int age){this.age = age;}

    public int compareTo(Object obj){
            // cast the obj as a Person -- we are only comparing people
            Person pObj = (Person)obj;
            // compare the last names using their compareTo methods
            return(lastName.compareTo(pObj.getLastName()));
    }
}

Then you can run your list of Person objects through a Collections.sort() method and you will have a list of people ordered by their last names. But what if, when you display your list of people, the user wants the ability to sort the list by the first name or the age? You could add a new property to your object called compareBy that takes a parameter used to identify which property of the object will be used in the comparison. Then in your compareTo() method you will need to base your comparison on the property that compareBy is pointing to. This is not a very clean approach. It would be better to use a java.util.Comparator implementation.

java.util.Comparator

The Comparator interface defines two method signatures for implementation, compare() that takes as parameters the two objects to be compared and equals() which takes an object to be compared to the Comparator. The return value of the compare() method is basically the same as that for the compareTo() method of the Comparable interface. The main difference between the Comparator and a Comparable object is that Comparators perform the comparison external to the objects being compared and therefore can be reusable over many different object types.

Let’s start out with our clean Person class again:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person {
    private String id, firstName, lastName;
    private int age;

    public String getId(){return(id);}
    public String getFirstName(){return(firstName);}
    public String getLastName(){return(lastName);}
    public int getAge(){return(age);}
    public void setId(String id){this.id = id;}
    public void setFirstName(String firstName){this.firstName = firstName;}
    public void setLastName(String lastName){this.lastName = lastName;}
    public void setAge(int age){this.age = age;}
}

and then define a comparator to do the work of the comparable Person we created (so we don’t lose any functionality).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LastNameComparator implements Comparator {

    public boolean equals(Object obj){
            // we're just going to say that any LastNameComparators are equal
            return(obj instanceof LastNameComparator);
    }

    public int compare(Object obj1, Object obj2){
            // cast both objects are Person
            Person p1 = (Person)obj1;
            Person p2 = (Person)obj2;

            // compare their lastNames
            return(p1.getLastName().compareTo(p2.getLastName()));
    }
}

That’s all it takes. You can run your List of Person objects through the version of Collections.sort() that accepts a List and a Comparator to sort your list by last name. You have gained something by doing this… you can now change the sort criteria on the fly. Let’s create a Comparator to compare by age.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class AgeComparator implements Comparator {

    public boolean equals(Object obj){
            // we're just going to say that any AgeComparators are equal
            return(obj instanceof AgeComparator);
    }

    public int compare(Object obj1, Object obj2){
            // cast both objects are Person
            Person p1 = (Person)obj1;
            Person p2 = (Person)obj2;

            // compare their ages
            int result = 0; // defaults to equal
            if(p1.getAge() > p2.getAge()){
                    result = 1;
            } else if(p1.getAge() < p2.getAge()){
                    result = -1;
            }

            return(result);
    }
}

Now you can use that instead of the LastNameComparator to order the Person objects by their ages. The Comparator at first seems like more coding, but if you design your Comparators well, you will be able to reuse them in the future, especially if you throw in some reflection. The Jakarta Commons Collections API has a set of useful Comparators, though I think one of the most useful Comparators is found in the Jakarta Commons Bean Utils API, called the BeanComparator. The BeanComparator uses reflection compare two objects based on the value of a specified property. Using the BeanComparator to perform our comparisons would be much simpler:

1
2
3
Collections.sort(people,new BeanComparator("lastName"));
// - or -
Collections.sort(people,new BeanComparator("age"));

Now how is that for simple and straight forward?

So that is a basic introduction to using the Comparable and Comparator interfaces. In general, it is better to use Comparators so that your comparison is not so tightly tied to your implementations.

Additional Reading

Sort it Out (Alex Blewitt, JavaWorld, 2002) – This article takes a little more broad top-level approach, but has some good information too.

Popularity: 1% [?]

  • Share/Bookmark

Tags: ,

Switch to our mobile site