Comparable and Comparator in Java with example

Comparable and Comparator in Java

In any programming language always there is a need to sort the elements of an array or a collection. We can sort array or collection of primitive and wrapper classes in natural order where the number will be sorted in ascending order and the string will be sorted in alphabetic order. But when there is a requirement to sort the custom object array or collection we cannot sort them without providing a sorting mechanism.

Observe the below code snippet where we will be able to sort an array of “int” using Arrays.sort() method. But when we supply an array of custom objects to Arrays.sort() it throws an exception. Read more about Exception.

package com.coderdesks.basic;

import java.util.Arrays;

public class ComparatorVsComparable {

	public static void main(String[] args) {
		
		// primitive array
		int [] numbers = {19,34,2,5,1,4,8,37,21};
		Arrays.sort(numbers);
                System.out.print("int [] after sort ");
		for(int i : numbers){
			System.out.print(i);
			System.out.print(" ");
		}
		System.out.println();
		
		System.out.println("sorting cusotm object array start");
		// custom object array
		Orders [] orders = {
				new Orders(212,2000.40, "Tom"),
				new Orders(132,2999.00, "Sam"),
				new Orders(245,2070.10, "Mark"),
				new Orders(653,2020.33, "Calvin")
		};
		
		Arrays.sort(orders);
		for(Orders order : orders){
			System.out.println(order);
			
		}
	}	
}

Output:

int [] after sort 1 2 4 5 8 19 21 34 37 

sorting custom object array start

Exception in thread "main" java.lang.ClassCastException: com.coderdesks.basic.Orders cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
	at java.util.Arrays.sort(Arrays.java:472)
	at com.coderdesks.basic.ComparatorVsComparable.main(ComparatorVsComparable.java:28)

To solve these problems Java provides two interfaces java.lang.Comparable and java.util.Comparator.

public interface Comparable<T> {
   public int compareTo(T o);
}

public interface Comparator<T> {

int compare(T o1, T o2);

boolean equals(Object obj);

}

Let’s understand these interfaces in more detail.

Comparable:

Java provides a Comparable interface that should be implemented by any custom class if we want to use Arrays or Collections sorting methods to sort the elements.

The Comparable interface has the compareTo(T o) method which is used for sorting. We should override this method in such a way that it returns a negative integer, zero, or a positive integer if “this” object is less than, equal to, or greater than the object passed as an argument.

Refer to the below example after the Orders class used above implements Comparable interface and overrides compareTo() method.

package com.coderdesks.basic;

public class Orders implements Comparable<Orders>
{

	private int orderId;
	
	private double amount;
	
	private String orderBy;

	public Orders(){
		
	}

	public Orders(int orderId, double amount, String orderBy) {
		super();
		this.orderId = orderId;
		this.amount = amount;
		this.orderBy = orderBy;
	}

	@Override
	public String toString() {
		return "Orders [orderId=" + orderId + ", amount=" + amount + ", orderBy=" + orderBy + "]";
	}

	@Override
	public int compareTo(Orders o) {
		return (this.orderId < o.orderId) ?-1 :((this.orderId == o.orderId) ? 0 : 1);
	}
}

Once we run the below class it will sort the array of Orders class objects.

package com.coderdesks.basic;

import java.util.Arrays;

public class ComparatorVsComparable {

	public static void main(String[] args) {
		
		System.out.println("Default sorting of orders array");
		Orders [] orders = {
				new Orders(212,2000.40, "Tom"),
				new Orders(132,2999.00, "Sam"),
				new Orders(245,2070.10, "Mark"),
				new Orders(653,2020.33, "Calvin")
		};
		
		Arrays.sort(orders);
		for(Orders order : orders){
			System.out.println(order);
			
		}
	}	
}

Output:

Default sorting of orders array
Orders [orderId=132, amount=2999.0, orderBy=Sam]
Orders [orderId=212, amount=2000.4, orderBy=Tom]
Orders [orderId=245, amount=2070.1, orderBy=Mark]
Orders [orderId=653, amount=2020.33, orderBy=Calvin]

Here you can see the array of Orders is sorted based on the orderId.

So far so good, we have to sort the Orders array by “orderId” and we can do it. But in most of the real-life scenarios, we want sorting based on different parameters. Let’s take an example of the Orders class used above, where the owner may want to sort the orders by their amount, the person managing the reports may want to sort them by order id and someone else wants to sort them by name. In this case, if we use a Comparable interface that provides default sorting and we can’t change it dynamically, we can only sort using one of the fields.

Here Comparator interface comes into the picture. The Comparator interface allows you to provide multiple methods with different ways of sorting. We can choose the way we want to sort the elements.

Comparator:

The java.util.Comparator interface provides compare(Object o1,Object o2) method for sorting. To sort compare method needs to be implemented in such a way that it returns a negative integer if the first object is less than the second one, returns 0 if both are equal, and a positive integer if the first object is greater than the second one.

Let’s modify the Orders class by providing sorting using amount and order by as well. Observe the Orders class with the implementation of the Comparator along with the default sorting.

package com.coderdesks.basic;

import java.util.Comparator;

public class Orders implements Comparable<Orders>
{

	private int orderId;
	
	private double amount;
	
	private String orderBy;

	public Orders(){
		
	}

	public Orders(int orderId, double amount, String orderBy) {
		super();
		this.orderId = orderId;
		this.amount = amount;
		this.orderBy = orderBy;
	}

	@Override
	public String toString() {
		return "Orders [orderId=" + orderId + ", amount=" + amount + ", orderBy=" + orderBy + "]";
	}

	@Override
	public int compareTo(Orders o) {
		return (this.orderId < o.orderId) ?-1 :((this.orderId == o.orderId) ? 0 : 1);
	}
	
	public static Comparator<Orders> sortByAmount = new Comparator<Orders>() {
		
		@Override
		public int compare(Orders o1, Orders o2) {

			return (o1.amount < o2.amount) ? -1 : ( (o1.amount == o2.amount) ? 0 : 1);
		}
	};
	
	public static Comparator<Orders> sortByOrderBy = new Comparator<Orders>() {
		
		@Override
		public int compare(Orders o1, Orders o2) {

			return o1.orderBy.compareTo(o2.orderBy);
		}
	};
}

To check if the changes we made are working or not run the below code snippet.

package com.coderdesks.basic;

import java.util.Arrays;

public class ComparatorVsComparable {

	public static void main(String[] args) {
		
		System.out.println("Default sorting of orders array \n");
		Orders [] orders = {
				new Orders(212,2000.40, "Tom"),
				new Orders(132,2999.00, "Sam"),
				new Orders(245,2070.10, "Mark"),
				new Orders(653,2020.33, "Calvin")
		};
		
		Arrays.sort(orders);
		for(Orders order : orders){
			System.out.println(order);
			
		}
		
		System.out.println(" ");

		System.out.println("Sorting of orders array using amount \n");
		
		Arrays.sort(orders, Orders.sortByAmount);
		
		for(Orders order : orders){
			System.out.println(order);
			
		}
		System.out.println(" ");
		
		System.out.println("Sorting of orders array using order by \n");
		
		Arrays.sort(orders, Orders.sortByOrderBy);
		
		for(Orders order : orders){
			System.out.println(order);
			
		}
	}
}

Output:

Default sorting of orders array 

Orders [orderId=132, amount=2999.0, orderBy=Sam]
Orders [orderId=212, amount=2000.4, orderBy=Tom]
Orders [orderId=245, amount=2070.1, orderBy=Mark]
Orders [orderId=653, amount=2020.33, orderBy=Calvin]
 
Sorting of orders array using the amount 

Orders [orderId=212, amount=2000.4, orderBy=Tom]
Orders [orderId=653, amount=2020.33, orderBy=Calvin]
Orders [orderId=245, amount=2070.1, orderBy=Mark]
Orders [orderId=132, amount=2999.0, orderBy=Sam]
 
Sorting of orders array using order by 

Orders [orderId=653, amount=2020.33, orderBy=Calvin]
Orders [orderId=245, amount=2070.1, orderBy=Mark]
Orders [orderId=132, amount=2999.0, orderBy=Sam]
Orders [orderId=212, amount=2000.4, orderBy=Tom]

We can even provide the Comparator implementation in a separate class. This is the major advantage of the Comparator interface you can use it with third-party classes or the class you aren’t allowed to modify in your code.

Let’s modify our Orders class by adding a date field orderDate and providing the sorting based on orderDate using a separate class. In this example, we will also use Collections.sort() method to sort the list of orders.

After modifying the Orders class it will look as below.

package com.coderdesks.basic;

import java.util.Comparator;
import java.util.Date;

public class Orders implements Comparable<Orders>
{

	private int orderId;
	
	private double amount;
	
	private String orderBy;
	
	private Date orderDate;

	public Orders(){
		
	}

	public Orders(int orderId, double amount, String orderBy, Date orderDate) {
		super();
		this.orderId = orderId;
		this.amount = amount;
		this.orderBy = orderBy;
		this.orderDate = orderDate; 
	}


	@Override
	public String toString() {
		return "Orders [orderId=" + orderId + ", amount=" + amount + ", orderBy=" + orderBy + ", orderDate=" + orderDate
				+ "]";
	}

	@Override
	public int compareTo(Orders o) {
		return (this.orderId < o.orderId) ?-1 :((this.orderId == o.orderId) ? 0 : 1);
	}
	
	public static Comparator<Orders> sortByAmount = new Comparator<Orders>() {
		
		@Override
		public int compare(Orders o1, Orders o2) {

			return (o1.amount < o2.amount) ? -1 : ( (o1.amount == o2.amount) ? 0 : 1);
		}
	};
	
	public static Comparator<Orders> sortByOrderBy = new Comparator<Orders>() {
		
		@Override
		public int compare(Orders o1, Orders o2) {

			return o1.orderBy.compareTo(o2.orderBy);
		}
	};

	public int getOrderId() {
		return orderId;
	}

	public double getAmount() {
		return amount;
	}

	public String getOrderBy() {
		return orderBy;
	}

	public Date getOrderDate() {
		return orderDate;
	}	
}

Here is the “OrdersDateComparator” class providing implementation to compare the method of the Comparator interface.

package com.coderdesks.basic;

import java.util.Comparator;

public class OrdersDateComparator implements Comparator<Orders>{

	@Override
	public int compare(Orders o1, Orders o2) {

		return o1.getOrderDate().compareTo(o2.getOrderDate());
	}
}

To test the above changes let run the below code snippet.

package com.coderdesks.basic;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

public class ComparatorVsComparable {

	public static void main(String[] args) {
		
		Orders [] orders = {
				new Orders(212,2000.40, "Tom",getDateFromString("21-09-2019")),
				new Orders(132,2999.00, "Sam",getDateFromString("12-09-2019")),
				new Orders(245,2070.10, "Mark",getDateFromString("18-08-2019")),
				new Orders(653,2020.33, "Calvin",getDateFromString("01-04-2019"))
		};		
		
		System.out.println("Sorting of orders list using order date \n");
		
		List<Orders> orderList = Arrays.asList(orders);
		
		Collections.sort(orderList,new OrdersDateComparator());

		for(Orders order : orderList){
			System.out.println(order);
			
		}
		
	}

	private static Date getDateFromString(String strDate){
		try {
			return new SimpleDateFormat("dd-MM-yyyy").parse(strDate);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}

Output:

Sorting of orders list using order date 

Orders [orderId=653, amount=2020.33, orderBy=Calvin, orderDate=Mon Apr 01 00:00:00 IST 2019]
Orders [orderId=245, amount=2070.1, orderBy=Mark, orderDate=Sun Aug 18 00:00:00 IST 2019]
Orders [orderId=132, amount=2999.0, orderBy=Sam, orderDate=Thu Sep 12 00:00:00 IST 2019]
Orders [orderId=212, amount=2000.4, orderBy=Tom, orderDate=Sat Sep 21 00:00:00 IST 2019]

ComparableComparator
For Comparable Arrays.sort() and Collections.sort() method automatically use compareTo() method. For Comparator client has to provide the implementation of Comparator interface.
java.lang.Comparablejava.util.Comparator
Method compareTo(Object o) takes only one argumentMethod compare(Object o1,Object o2) takes two arguments
Provides single way of sorting also called default way. Provides methods to sort based on different parameters
The class has to implement Comparable interface to enable sortingWe can sort even after implementing Comparator in a separate class. This features allows us to sort third party classes well.

Happy Learning !!

Leave a Comment