May 21, 2022

Comparable Vs Comparator in Java

Comparable and Comparator in Java are two interfaces used to sort a collection of objects. The obvious question is why two interfaces when they both do the same task of sorting the objects. In this post we’ll see what are Comparable and Comparator interfaces in Java, why both are required and differences between Comparable and Comparator in Java.

Comparable interface in Java

Comparable interface in Java has a single method compareTo().

int compareTo(T o)- This method compares the object used to call the method with the object passed as parameter for ordering.

The objects of the class implementing this interface are ordered as per the implementation of the compareTo() method. Since the class itself is implementing the interface so the ordering as provided by Comparable interface is referred to as the class's natural ordering.

Lists (and arrays) storing objects of class that implements this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map i.e TreeMap or as elements in a sorted set i.e. TreeSet, without the need to specify a Comparator.

Comparator interface in Java

Comparator interface in Java has a method compare().

int compare(T o1, T o2)- Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

Comparator interface is not part of the class which is sorted using the ordering provided by Comparator interface implementation. Comparator interface is implemented as a separate class or as an anonymous class.

Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order.

Comparable and Comparator Java example

Let us see an example to see how sort ordering is done using Comparable and Comparator and why both of them are needed.

There is a Person class with fields firstName, lastName and age. You want to sort Person class objects using the firstName + lastName so the class implements Comparable interface and provide logic for that ordering by overriding the compareTo() method.

public class Person implements Comparable {
  private String firstName;
  private String lastName;
  private int age;
  Person(String firstName, String lastName, int age){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  @Override
  // For sort order - sort on first name, 
  // if equal then sort on lastname
  public int compareTo(Person o) {
    int comp = this.getFirstName().compareTo(o.getFirstName());
    return comp != 0 ? comp :  this.getLastName().compareTo(o.getLastName());
  }

  @Override
  public String toString() {        
    return getFirstName() + " " + getLastName() + " " + getAge();
  }
}

Following class is used to create Person class objects and do the sorting by calling the Collections.sort() method. Since the objects stored in the List are already implementing Comparable interface so the objects will be sorted by using that implementation.

public class SortDemo {
  public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("William", "Shatner", 60));
    personList.add(new Person("William", "Brooks", 25));
    personList.add(new Person("Persis", "Khambatta", 50));
    personList.add(new Person("James", "Doohan", 70));
    personList.add(new Person("DeForest", "Kelley", 65));

    System.out.println("-- Original List --");
    for(Person person : personList){
     System.out.println("" + person);
    }
    // Sort the list
    Collections.sort(personList);
    System.out.println("--Sorted List--");
    for(Person person : personList){
      System.out.println("" + person);
    } 
  }
}
Output
-- Original List --
William Shatner 60
William Brooks 25
Persis Khambatta 50
James Doohan 70
DeForest Kelley 65
--Sorted List--
DeForest Kelley 65
James Doohan 70
Persis Khambatta 50
William Brooks 25
William Shatner 60

So far you have your class, class’ natural ordering is also defined by implementing the Comparable interface.

Now suppose you have a new requirement to sort the Person class by age or by last name + first name. Person class is already coupled with the Comparable interface and the ordering imposed by that interface. In this sort of scenario you can use Comparator interface, since Comparator can be implemented by another class so you can create different Comparator classes for different sort ordering. Then you can use the Collections.sort() method where Comparator is also passed to sort the objects.

public class Person implements Comparable<Person> {
  private String firstName;
  private String lastName;
  private int age;
  Person(String firstName, String lastName, int age){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  @Override
  // For sort order - sort on first name, 
  // if equal then sort on lastname
  public int compareTo(Person o) {
    int comp = this.getFirstName().compareTo(o.getFirstName());
    return comp != 0 ? comp :  this.getLastName().compareTo(o.getLastName());
  }

  @Override
  public String toString() {        
    return getFirstName() + " " + getLastName() + " " + getAge();
  }
}

// Sorting using age field
class AgeComparator implements Comparator<Person>{
  @Override
  public int compare(Person o1, Person o2) {
    return o1.getAge() >= (o2.getAge()) ? 1 : -1;    
  }    
}
//Sorting using last name if equal then sort on firstName
class NameComparator implements Comparator<Person>{
  @Override
  public int compare(Person o1, Person o2) {
    int comp = o1.getLastName().compareTo(o2.getLastName());
    if(comp == 0){
      comp = o1.getFirstName().compareTo(o2.getFirstName());
    }
    return comp;
  }    
}

As you can see two new Comparator classes are added one that sorts by age and another sorts by last name. Person class still has its Comparable interface implementation defining its natural ordering.

public class SortDemo {
  public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("William", "Shatner", 60));
    personList.add(new Person("William", "Brooks", 25));
    personList.add(new Person("Persis", "Khambatta", 50));
    personList.add(new Person("James", "Doohan", 70));
    personList.add(new Person("DeForest", "Kelley", 65));
       
    System.out.println("-- Original List --");
    for(Person person : personList){
     System.out.println("" + person);
    }
		 
    // Sort the list by age
    Collections.sort(personList, new AgeComparator());
    System.out.println("--Sorted List by Age--");
    for(Person person : personList){
     System.out.println("" + person);
    } 
     
    // Sort the list by last name
    Collections.sort(personList, new NameComparator());
    System.out.println("--Sorted List by Last Name--");
    for(Person person : personList){
     System.out.println("" + person);
    } 
		 
    // Sort the list by first name - Natural ordering
    Collections.sort(personList);
    System.out.println("--Sorted List by first name--");
    for(Person person : personList){
      System.out.println("" + person);
    } 
  }
}
Output
-- Original List --
William Shatner 60
William Brooks 25
Persis Khambatta 50
James Doohan 70
DeForest Kelley 65
--Sorted List by Age--
William Brooks 25
Persis Khambatta 50
William Shatner 60
DeForest Kelley 65
James Doohan 70
--Sorted List by Last Name--
William Brooks 25
James Doohan 70
DeForest Kelley 65
Persis Khambatta 50
William Shatner 60
--Sorted List by first name--
DeForest Kelley 65
James Doohan 70
Persis Khambatta 50
William Brooks 25
William Shatner 60

As you can see now if you want to sort by age field then you can pass the Comparator that sorts by age in the Collections.sort() method. Same way if you want to sort by lastName field then you can pass the Comparator that sorts by lastName in the Collections.sort() method. If you want to sort by firstName you still have that implementation provided by the implementation of the Comparable interface.

Difference between Comparable and Comparator in Java

Comparable Comparator
Comparable interface has compareTo() method for providing sorting logic. Comparator interface has compare() method for providing sorting logic.
Comparable interface is in java.lang package. Comparator interface is in java.util package.
Comparable interface is implemented by the class whose objects are to be sorted. Comparator interface is implemented by some other class or as an anonymous class.
Since the class whose objects are to be sorted implements the Comparable interface so Comparable can provide only one way of sorting the objects. There can be many Comparator classes providing logic for different sort ordering.
A List or array having objects that implement the Comparable interface can be sorted by just passing the List in Collections.sort() or by using Arrays.sort() for array. In case of Comparator, instance of Comparator has to be passed with the Collections.sort() method.

That's all for the topic Comparable Vs Comparator in Java. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment