Comparing objects

In real life, objects are often comparable. For example, one car is more expensive than another, this dictionary is thicker than those books, Granny is older than Auntie Mollie (well, yeah, living objects, too, are comparable), and so forth.

In writing object-oriented programs, there are often needs to compare instances of the same class. And once instances are comparable, they can be sorted. As an example, given two Employees, you may want to know which one has stayed in the organization longer. Or, in a search method for Person instances with a first name of Larry, you may want to display search results sorted by age. This article teaches you how to design your class to make its instances comparable by using the java.lang.Comparable  interface and presents examples.

Most Java programmers know how to sort the elements of a String array by using the sort method of java.util.Arrays. For String instances in an ArrayList, you can sort them with the sort method of the java.util.Collections class. The code in Listing 1 shows how to use Arrays.sort to order String instances.

Listing 1: Sorting String Instances Using Arrays.sort

import java.util.Arrays;  . . .  
String animals[] = new String[4]; 
animals[0] = "snake"; 
animals[1] = "kangaroo"; 
animals[2] = "wombat"; 
animals[3] = "bird";  
for (int i=0; i<4; i++) {   
  System.out.println("animal " + i + " : " + animals[i]); 
  }  
Arrays.sort(animals);  
for (int i=0; i<4; i++) {   
  System.out.println("animal " + i + " : " + animals[i]); 
  }
aussie anijmals

If you run the program, the first for loop gives you the name of animals as follows:

animal 0 : snake 
animal 1 : kangaroo 
animal 2 : wombat 
animal 3 : bird

And, the second for loop prints the animals sorted alphabetically.

animal 0 : bird 
animal 1 : kangaroo 
animal 2 : snake 
animal 3 : wombat

The general contract of Comparable.compareTo(o) is to return

  • a positive integer if this is greater than the other object.
  • a negative integer if this is lower than the other object.
  • 0 if this is equals to the other object.

With the java.util.Collections class's sort method, you can sort String instances in an ArrayList, as shown in Listing 2.

Listing 2: Sorting String Instances Using Collections.sort

import java.util.ArrayList; 
import java.util.Collections;  . . .  
ArrayList insects = new ArrayList(); 
insects.add("mosquito"); 
insects.add("butterfly"); 
insects.add("dragonfly"); 
insects.add("fly");  
int size = insects.size(); 
for (int i=0; i<size; i++) {   
  System.out.println("insect " + i + " : " + (String) insects.get(i)); 
  }  
Collections.sort(insects);  
for (int i=0; i<size; i++) {   
  System.out.println("insect " + i + " : " + (String) insects.get(i)); 
  }

The first for loop in Listing 2 produces the following output:

insect 0 : mosquito 
insect 1 : butterfly 
insect 2 : dragonfly 
insect 3 : fly

The second for loop prints the following:

insect 0 : butterfly 
insect 1 : dragonfly 
insect 2 : fly 
insect 3 : mosquito
insects

However, suppose we have a Person class, as below.

Listing 3: The Person Class

class Person {   
  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;   } 
   }

In another part of the program, we construct four instances of the Person class and populate them with names and ages:

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);

How do we sort these Person instances by age or by name?
Using the java.util.Arrays class' sort method, as in:

Arrays.sort(persons);

will throw a ClassCastException.

You can, of course, write your own code to sort them using an algorithm such as quick sort, bubble sort, or others, but that's impractical. The easy solution is to implement the java.lang.Comparable interface.

Using the java.lang.Comparable Interface

Implement the Comparable interface to make class instances comparable. This interface has one method, compareTo, which determines how to compare two instances of the class. The signature of this method is:

public int compareTo(Object o)

The compareTo method accepts Object, so you can pass it an instance of any type. However, chances are that you want to make sure to compare two instances of the same type. It does not make sense to compare an elephant with an ant, for example. Therefore, you can throw a java.lang.ClassCastException if the argument of this method is not the same type as your class.

The compareTo method returns zero if the object passed is equal to this instance. It returns a positive integer or a negative integer if this object is greater or smaller than the passed object, respectively.

Let's have a look at the examples in Listing 4 and Listing 5. Listing 4 presents a Person class that implements the Comparable interface. Notice that a Person object is older if its age value is greater than the object compared. Listing 5 shows the Testing class that constructs four instances of the Person class and sorts them by age. Both classes in Listings 4 and 5 reside in the comparable.ex01 package.

Listing 4: The Person Class That Implements the Comparable Interface

  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) {     
     int anotherPersonAge = (Person)anotherPerson.getAge();       
     return this.age - anotherPersonAge;       }
   }

Listing 5: The comparable.ex01.Testing Class

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);      
     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 code in Listing 5 is as follows:

Natural Order 
Goodyear, Elvis. Age:56 
Clark, Stanley. Age:8 
Graff, Jane. Age:16 
Goodyear, Nancy. Age:69  

Sorted by age 
Clark, Stanley. Age:8 
Graff, Jane. Age:16 
Goodyear, Elvis. Age:56 
Goodyear, Nancy. Age:69

modifed from onjava.com/pub/a/onjava/2003/03/12/java_comp.html

Your turn

Write a compareTo method for the CertainDay class below:

public class CertainDay implements Comparable<CertainDay> {
  int year;
  int month;
  int day;
  public CertainDay (int y, int m, int d){
    year = y; month = m; day = d; }
  public int getYear() { return year; }
  public int getMonth() { return month; }
  public int getDay() { return day;}
  public String toString(){ return month+"/"+day+"/"+year; }
  public int compareTo(CertainDay anotherDay){
     // complete the code
  }
}
calendar

 

The general contract of Comparable.compareTo(o) is to return

  • a positive integer if this is greater than the other object.
  • a negative integer if this is lower than the other object.
  • 0 if this is equals to the other object.

Test your code

import java.util.ArrayList; 
import java.util.Collections;
public class TestDays{
   public static void main(String[] args){
ArrayList<CertainDay> days = new ArrayList<CertainDay>();
days.add(new CertainDay(2018,3,8));
days.add(new CertainDay(2018,3,12));
days.add(new CertainDay(2015,6,2));
days.add(new CertainDay(2015,5,2));
days.add(new CertainDay(2014,6,2));
days.add(new CertainDay(2015,9,17));
days.add(new CertainDay(2017,10,30));
System.out.println("original list: "+days);
Collections.sort(days);
System.out.println("sorted list: "+days);
}
}