JavaData & JavaHow To Use Java Comparator

How To Use Java Comparator

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Java API library provides interfaces solely to establish a system of ordering on the objects of a class. They can be imposed implicitly as its natural principle of ordering or explicitly appended to customize the order as per requirement. This principle of ordering relies on the meaningful comparison method as implemented by the classes. These interfaces are basically comparators: one provided in the java.lang library, and another in the java.util package called Comparable and Comparator, respectively.

Comparable Interface

This interface is a part of java.lang package. Many classes in the Java API library implement this interface, such as Byte, Character, Double, Float, Long, Short, String, Integer, and so forth. This enables a list of objects of these classes to be sorted automatically by the Collection.sort function. Implementation of this interface enables the objects of this class to be used as keys in a sorted map or elements in a sorted set without using any explicit comparators. Any class that wants to impose ordering on the objects implements this interface. It is a generic interface; and the most important method declared by it is:

int compareTo(T object)

This method determines the natural ordering of instances of a class. For example, the natural ordering of a String, Integer, or a Date is deemed to be in ascending order.

package org.mano.example;

import java.util.Set;
import java.util.TreeSet;

public class Main {

   public static void main(String[] args) {

      Set<String> set = new TreeSet<>();
      set.add(new String("Australia"));
      set.add(new String("UK"));
      set.add(new String("Canada"));
      set.add(new String("US"));
      set.add(new String("Finland"));
      set.add(new String("Costa Rica"));
      for (String str : set) {
         System.out.println(str);
      }
   }
}

Output:

Australia
Canada
Costa Rica
Finland
UK
US

Observe how elements are automatically ordered alphabetically as per the natural ordering principle, from lowest to highest. We have not used the Comparable interface explicitly in the code. This ordering is possible because the String class implements the Comparable interface; the Set class just took advantage of it. There are numerous classes in the Java API library that implement this interface and and use them where it makes sense.

We can customize the compareTo functionality in the following way.

package org.mano.example;

public class implements Comparable<Product> {

   private String productName;
   private int totalSale;

   public Product(String n, int r) {
      productName = n;
      totalSale = r;
   }

   public String getProductName() {
      return productName;
   }

   @Override
   public int compareTo(Product o) {
      return totalSale - o.totalSale;
   }

   public static void main(String[] args) {
      Product p1 = new Product("Nvidia GTX 750", 9);
      Product p2 = new Product("Nvdia GTX 1080", 7);
      if (p1.compareTo(p2) < 0)
         System.out.println(p2.getProductName()
            + "has higher demand");
      else if (p1.compareTo(p2) > 0)
         System.out.println(p1.getProductName()
            + " has higher demand");
      else
         System.out.println(p1.getProductName() + " and "
            + p2.getProductName() + " have equal demand");
   }

}

Comparator Interface

What if we are required to compare two instances of a class that do not implement the Comparable interface? Also, Comparable imposes the natural ordering principle, which may not be appropriate under some other circumstances where we may not follow a predefined order sanctioned by the object. To handle these situations, Java provides another interface, called Comparator, defined in the java.util package. This interface enables us to write comparison code that is external to the class, in the following manner.

package org.mano.example;

public class Product {

   private String productName;
   private int totalSale;

   public Product(String n, int r) {
      productName = n;
      totalSale=r;
   }

   public String getProductName() {
      return productName;
   }

   public int getTotalSale() {
      return totalSale;
   }
}

package org.mano.example;

import java.util.Comparator;

public class SalesCompare implements
      Comparator<Product> {

   @Override
   public int compare(Product o1, Product o2) {
      return o1.getTotalSale() - o2.getTotalSale();
   }

}

package org.mano.example;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List<Product> list=new ArrayList<>();
      list.add(new Product("Nvidia GTX 750", 9));
      list.add(new Product("Nvdia GTX 1080", 4));
      list.add(new Product("Nvdia GTX 650", 7));
      list.add(new Product("Nvdia GTX 1020", 8));

      Collections.sort(list, new SalesCompare());

      System.out.println("Sales order:");
      for(Product p: list){
         System.out.println(p.getProductName()+" @ "+
            p.getTotalSale());
      }

   }
}

Output:

Sales order:
Nvdia GTX 1080 @ 4
Nvdia GTX 650 @ 7
Nvdia GTX 1020 @ 8
Nvidia GTX 750 @ 9

Observe how a class that was not initially designed keeping the paradigm of comparison in mind can still leverage the Comparator interface with a separate class. This means the Comparator interface gives the power to establish a comparison function without the slightest need to design a class accordingly. It can always be extended at a later point in time, or as needed.

There is another interesting aspect of the Comparator interface. It is a functional interface defined as follows:

@FunctionalInterface
public interface Comparator<T>

And, like any other functional interface, it has exactly one abstract method representing a single function construct. Elaborating the definition and conceptual background of functional interface is out of the scope of this article; the interested reader may refer to the Java docs and Java Language Specification for a more in-depth view.

Using a functional interface empowers this interface to be used with lambda expressions, method references, and the constructor reference. It, however, can still work like any other interface, including its usage with anonymous classes, factory methods, normal implementation with classes, and so on.

Let’s find out how many different ways we can write the above code, some due to the power of the functional interface.

This is what we have done:

// ...
List<Product> list=new ArrayList<>();
list.add(new Product("Nvidia GTX 750", 9));
list.add(new Product("Nvdia GTX 1080", 4));
list.add(new Product("Nvdia GTX 650", 7));
list.add(new Product("Nvdia GTX 1020", 8));

Collections.sort(list, new SalesCompare());

System.out.println("Sales order:");
for(Product p: list){
   System.out.println(p.getProductName()+
      " @ "+p.getTotalSale());
}
// ...

Another way to this is as follows;

Collections.sort(list, new Comparator<Product>() {
   @Override
   public int compare(Product o1, Product o2) {
      return o1.getTotalSale() - o2.getTotalSale();
   }
});

System.out.println("Sales order2:");
for (Product p : list) {
   System.out.println(p.getProductName() +
      " @ " + p.getTotalSale());
}

Another way, using lambda expression,

Collections.sort(list, (Product o1, Product o2)
   -> o1.getTotalSale() - o2.getTotalSale());

System.out.println("Sales order3:");
for (Product p : list) {
   System.out.println(p.getProductName()
      + " @ " + p.getTotalSale());
}

Conclusion

In Java, there are classes such as TreeSet or TreeMap of the Collection API library that store elements in a sorted order. This ordering of elements is actually defined by the comparators. The order imposed is called natural because we normally expect B to come after A or 2 to come after 1. This ascending ordering is typically expected with elements when arranged in a sequence, hence called the natural order. However, if we want to order elements in a different way or be based upon different logic, we should specify a Comparator during the construction of the classes. The functional interface annotation used with the Comparator interface is just for usage convenience with lambda expression.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories