Comparing Objects in Java - Comparators and Comparable

Creating our own classes that represent real world entities are a basic coding task nowadays. For example, a project dealing with ecommerce apps will need to create Product, Category, Cart etc. kinds on user defined classes. A finance app will contain Fund, Account etc. classes. 

All these classes will contain information related to the entity they represent. As an example, we can build a Product class that may have information like product Id, product name, category, sub products, price, created by etc. 

These user-defined classes are objects, that are created with information or data about the instance itself. Objects are often combined or grouped together as per the requirement. For example, objects of Product class can be grouped in an Array or ArrayList.

Now, imagine you want to see the list of products. Simple, iterate over the list and print the product values. However, user wants to see the list in order of ascending order of names or more realistically, in order of price. In another situation we would want to see a  list of students in order of their ranks. 

These are very basic requirements in day to day life and normally this can be achieved either on the java side of the code or the database side. Both have their pros and cons and we'll discuss about them, probably some other time. 

Assuming we want to do this in java. Let us assume we have a class called Invoice. It has very basic fields that indicate the invoice belongs to a consumer, who has an amount due. He might have paid some amount as well. 

Invoice

Now, let's assume we are service providers that are charging the clients for our services. A very basic and most frequent operation will be to see the list of customers ordered with due date. This will make it possible for us to determine the customers whose due dates are near and we can send them a reminder. 

Another thing before we start, I am going to use sort method of Collections class. The sort method internally calls compare method that decides which comparing element is bigger. It checks the return value of the compare methods being called. They can be -ve, 0 or +ve based on which object property is greater.

Implementing a Comparable

Imagine comparing two Invoice objects. One of them calls a method on the other and that is how we compare them. This should be very similar to comparing the Integer wrapper classes. We are trying to achieve the same thing here.


Let's start by implementing the 'Comparable' interface in the Invoice class. Read more about Comparable here.

public class Invoice implements Serializable, Comparable<Invoice>

Once you have done the same lets start implementing the required method. If we look at the Interface code then we would know that it contains only one method that needs to be implemented.

public int compareTo(T o);

Implementing the same

@Override
public int compareTo(Invoice o) {
	return this.dueDate.compareTo(o.dueDate);
}

And that is it. All that we did here was to use the compareTo method of the LocalDate class to compare the objects. this is the object that is comparing the other object with.

Now we can start comparing Invoice objects too.

invoiceObject1.compareTo(invoiceObject2)

This is great. We can now start comparing the objects. Remember we are comparing the objects based on Due Date property. We can change that if we want. All we need to do return an int from the overriden compareTo method. This int determines if the two objects being compared are equal or not. This is what we'll call the natural ordering of the objects. 

To sort a list of invoice objects we can use the below.

Collections.sort(invoiceList);

Simple! The above will sort the entire list in ascending order of due date. You can look at the complete invoice class in github. The class could be overwhelming as it has a builder as well to construct the object, but you can ignore it. You can also ignore the Jackson annotation. All you need is a bean that has properties and getters/setters. Then you need to implement this interface and override the compareTo method. 

Creating a Comparator class

Now, we have another situation. Imagine a Product class for an ecommerce app. And you are looking at a list of laptops. You would want to sort all laptops based on price or by brands. You can also sort by RAM size or processor, or by number of reviews etc. The thing is that the requirements may present with a lot of other sorting options than just one. 

Let's imagine a situation, in our case, where we want to sort using another property of the Invoice object. What if you want to sort based on the amount due. Now, we have already used the compareTo method and hence, there needs to another way for us to decide what property should be used for sorting. A comparator comes handy here. 

Comparator is an external class that is used for comparing two objects. The idea to have multiple classes that have logic on comparing different objects based on different properties. Java reference for Comparators can be found here

It is very simple to write the class. Define a class and implement a Comparator interface. The implement the compare method.

public class InvoiceAmountDueComparator implements Comparator<Invoice> {

	public boolean orderAsc;

	/**
	 * Order to determine if sorting needs to be done in ascending order or not
	 * 
	 * @param orderAsc
	 */
	public InvoiceAmountDueComparator(boolean orderAsc) {
		this.orderAsc = orderAsc;
	}

	@Override
	public int compare(Invoice o1, Invoice o2) {
		if (orderAsc) {
			return Double.compare(o1.getAmountDue(), o2.getAmountDue());
		} else {
			return Double.compare(o2.getAmountDue(), o1.getAmountDue());
		}
	}

}

The above code indicates how simple a comparator can be to write. This class excepts a boolean parameter that during initialization. The parameter determines the sorting order.

To use this class, you need to pass this in Collections.sort. Similar to the comparable, except you also need to pass the comparator class. 

InvoiceAmountDueComparator invoiceAmountDueComparator = new InvoiceAmountDueComparator(true);
Collections.sort(invoiceList, invoiceAmountDueComparator);

Sorting is a very essential part of programming and needs to be correctly handled. I have tried highlighting some issues and requirements that can help you understand the exact need for the methods we discussed. You can however ignore the Collections.sort and write your own sorting logic as well. I believe this is an easier approach. 

Comments

Popular posts from this blog

JavaFX 15 Installation and Introduction

Insertion Sort