Open In App

Why to Use Comparator Interface Rather than Comparable Interface in Java?

Last Updated : 06 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In Java, the Comparable and Comparator interfaces are used to sort collections of objects based on certain criteria. The Comparable interface is used to define the natural ordering of an object, whereas the Comparator interface is used to define custom ordering criteria for an object.

Here are some reasons why you might want to use the Comparator interface over the Comparable interface in Java:

  1. Sorting based on different criteria: When using the Comparable interface, the natural ordering of an object is defined in the class itself. This means that you can only sort objects based on a single criterion. On the other hand, when using the Comparator interface, you can define multiple sorting criteria by creating different instances of the comparator interface. This provides more flexibility when sorting objects.
  2. Sorting objects that do not implement the Comparable interface: If you need to sort objects that do not implement the Comparable interface, you cannot use the Collections.sort() method, which requires objects to implement the Comparable interface. In this case, you can use the Collections.sort() method that takes a Comparator object as a parameter to sort the objects.
  3. Modifying sorting criteria at runtime: When using the Comparator interface, you can modify the sorting criteria at runtime by creating a new instance of the Comparator interface with different sorting logic. This provides more dynamic sorting behavior.
  4. Sorting objects in a different order: When using the Comparable interface, the objects are sorted in natural order, which is not always the desired behavior. When using the Comparator interface, you can define custom sorting criteria to sort objects in a different order.
  5. Separation of concerns: The Comparable interface defines the natural ordering of an object, which may not always be the desired behavior when sorting. When using the Comparator interface, the sorting behavior is separated from the object itself, which provides better encapsulation and separation of concerns.

In summary, the Comparator interface is more flexible and provides more control over the sorting behavior of objects. If you need to sort objects based on different criteria or if the objects do not implement the Comparable interface, you should use the Comparator interface. On the other hand, if you want to define the natural ordering of an object, you should use the Comparable interface.

Prerequisite: Comparable vs Comparator in Java

Throughout the entire lifecycle of the project, we will be creating multiple classes in Java according to our requirements, and sorting is much required in many situations, especially during the report generation, we need to display the data according to the sorted order either in ascending/descending order. Even report generation requires not only single-field sorted order but also multiple fields and also multi-variant sort mechanism is also needed. In that case, we need to depend on Java Comparator Interface which is available in java.util package. Let us take a sample maven java project containing JUnit dependency to look at this feature and justify whether the sorting happens according to our needs or not by using JUnit.

Example Maven Project

Project Structure:

Project Structure

 

This is the maven project containing JUNIT dependencies in

pom.xml

XML
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.gfg.ComparatorServicesInJava</groupId><artifactId>ComparatorServicesInJava</artifactId><packaging>jar</packaging><version>1.0-SNAPSHOT</version><properties><!-- https://maven.apache.org/general.html#encoding-warning --><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><junit.version>5.3.1</junit.version><pitest.version>1.4.3</pitest.version></properties><dependencies><!-- junit 5, unit test --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>${junit.version}</version><scope>test</scope></dependency></dependencies><build><finalName>maven-mutation-testing</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.0.0-M1</version></plugin><plugin><groupId>org.pitest</groupId><artifactId>pitest-maven</artifactId><version>${pitest.version}</version><executions><execution><id>pit-report</id><phase>test</phase><goals><goal>mutationCoverage</goal></goals></execution></executions><!-- https://github.com/hcoles/pitest/issues/284 --><!-- Need this to support JUnit 5 --><dependencies><dependency><groupId>org.pitest</groupId><artifactId>pitest-junit5-plugin</artifactId><version>0.8</version></dependency></dependencies><configuration><targetClasses><param>com.gfg.ComparatorServicesInJava.*ComparatorServicesInJava*</param></targetClasses><targetTests><param>com.gfg.ComparatorServicesInJava.*</param></targetTests></configuration></plugin></plugins></build></project>

Let’s start with the bean class that contains comparisons i.e. sort by  in separate  classes and each class implements a comparator as well specific to

  • Companyname
  • Salary
  • Designation
  • Designation and Salary

Recruitment.java

Java
importjava.util.ArrayList;importjava.util.Collections;importjava.util.Comparator;publicclassRecruitment{publicRecruitment(StringcompanyName,StringemployeeName,Stringdesignation,doublesalary,booleanstatus){super();this.companyName=companyName;this.employeeName=employeeName;this.designation=designation;this.salary=salary;this.status=status;}publicRecruitment(){// via setter methods, rest fields are done}StringcompanyName;Stringdesignation;StringemployeeName;doublesalary;booleanstatus;publicStringgetCompanyName(){returncompanyName;}publicvoidsetCompanyName(StringcompanyName){this.companyName=companyName;}publicStringgetDesignation(){returndesignation;}publicvoidsetDesignation(Stringdesignation){this.designation=designation;}publicStringgetEmployeeName(){returnemployeeName;}publicvoidsetEmployeeName(StringemployeeName){this.employeeName=employeeName;}publicdoublegetSalary(){returnsalary;}publicvoidsetSalary(doublesalary){this.salary=salary;}publicbooleanisStatus(){returnstatus;}publicvoidsetStatus(booleanstatus){this.status=status;}// Method of Recruitment class// To print Recruitment details in main()publicStringtoString(){// Returning attributes of Recruitmentreturnthis.companyName+" "+this.employeeName+" "+this.designation+" "+this.salary+" "+this.status;}publicstaticvoidmain(Stringargs[]){// Creating an empty ArrayList of Student typeArrayList<Recruitment>list=newArrayList<Recruitment>();// Adding entries in above List// using add() methodlist.add(newRecruitment("abc consultants","Rachel","Analyst",100000.0,true));list.add(newRecruitment("nyc consultants","Monica","DBA",90000.0,true));list.add(newRecruitment("abc consultants","Phoebe","Programmer",70000.0,true));list.add(newRecruitment("nj consultants","Jane","Programmer",80000.0,false));list.add(newRecruitment("xyz consultants","Ross","ProgramManager",200000.0,true));list.add(newRecruitment("chennai consultants","Chandler","ProjectManager",150000.0,true));list.add(newRecruitment("xyz consultants","Joe","Programmer",80000.0,true));list.add(newRecruitment("mumbai consultants","Any","Programmer",85000.0,false));// Display message on console for better readabilitySystem.out.println("Unsorted order of the details");// Iterating over entries to print themfor(inti=0;i<list.size();i++)System.out.println(list.get(i));// Sorting the entries by company nameCollections.sort(list,newGetDataByCompanyName());System.out.println("\nSorted by CompanyName");// We will get the sorted order now by company namefor(inti=0;i<list.size();i++)System.out.println(list.get(i));// Sorting the entries by salaryCollections.sort(list,newGetDataBySalary());System.out.println("\nSorted by salary");// We will get the sorted order now by salaryfor(inti=0;i<list.size();i++)System.out.println(list.get(i));// Sorting the entries by designationCollections.sort(list,newGetDataByDesignation());System.out.println("\nSorted by designation");// We will get the sorted order now by designationfor(inti=0;i<list.size();i++)System.out.println(list.get(i));// Sorting the entries by designation and salaryCollections.sort(list,newGetDataByDesignationAndSalary());System.out.println("\nSorted by designation and salary");// We will get the sorted order now by salaryfor(inti=0;i<list.size();i++)System.out.println(list.get(i));}}classGetDataByCompanyNameimplementsComparator<Recruitment>{@Overridepublicintcompare(Recruitmentrecruitment1,Recruitmentrecruitment2){returnrecruitment1.companyName.compareTo(recruitment2.companyName);}}classGetDataBySalaryimplementsComparator<Recruitment>{@Overridepublicintcompare(Recruitmentrecruitment1,Recruitmentrecruitment2){return(int)(recruitment1.salary-recruitment2.salary);}}classGetDataByDesignationimplementsComparator<Recruitment>{@Overridepublicintcompare(Recruitmentrecruitment1,Recruitmentrecruitment2){returnrecruitment1.designation.compareTo(recruitment2.designation);}}// Using 2 fields to compareclassGetDataByDesignationAndSalaryimplementsComparator<Recruitment>{@Overridepublicintcompare(Recruitmentrecruitment1,Recruitmentrecruitment2){intdesignationComparision=recruitment1.designation.compareTo(recruitment2.designation);intsalaryComparision=(int)(recruitment1.salary-recruitment2.salary);return(salaryComparision==0)?designationComparision:salaryComparision;}}

As we are implementing java.util.comparator, not only the natural order of sorting, based on our requirements, we can sort based on different fields. Hence 4 different classes are introduced to produce the result. Not only on a single field but on more than 1 field also sorting is possible. These are the advantages of using a comparator. If needed, we can put some test data and check for the same in the console

 

Now let us see the testcases to justify the same.

TestComparatorServicesJava.java

Java
import staticorg.junit.jupiter.api.Assertions.assertEquals;import staticorg.junit.jupiter.api.Assertions.assertTrue;importjava.util.ArrayList;importjava.util.Collections;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;publicclassTestComparatorServicesJava{privatevoidaddListElements(ArrayList<Recruitment>list){// Adding entries in above List// using add() methodlist.add(newRecruitment("abc consultants","Rachel","Analyst",100000.0,true));list.add(newRecruitment("nyc consultants","Monica","DBA",90000.0,true));list.add(newRecruitment("abc consultants","Phoebe","Programmer",70000.0,true));list.add(newRecruitment("nj consultants","Jane","Programmer",80000.0,false));list.add(newRecruitment("xyz consultants","Ross","ProgramManager",200000.0,true));list.add(newRecruitment("chennai consultants","Chandler","ProjectManager",150000.0,true));list.add(newRecruitment("xyz consultants","Joe","Programmer",80000.0,true));list.add(newRecruitment("mumbai consultants","Any","Programmer",85000.0,false));}@DisplayName("Test check whether sorting done by company name ")@TestpublicvoidtestSortByCompanyName(){// Creating an empty ArrayList of Recruitment typeArrayList<Recruitment>list=newArrayList<Recruitment>();addListElements(list);// Sorting the entries by company nameCollections.sort(list,newGetDataByCompanyName());// Assert the data for sorting by company nameassertEquals(true,list.get(0).getCompanyName().equalsIgnoreCase("abc consultants"));assertEquals(true,list.get(1).getEmployeeName().equalsIgnoreCase("Phoebe"));assertEquals(true,list.get(2).getDesignation().equalsIgnoreCase("ProjectManager"));assertTrue(list.get(0).getEmployeeName().equalsIgnoreCase("Rachel"));assertTrue(list.get(list.size()-1).getCompanyName().equalsIgnoreCase("xyz consultants"));assertTrue(list.get(list.size()-1).getDesignation().equalsIgnoreCase("Programmer"));}@DisplayName("Test check whether sorting done by salary ")@TestpublicvoidtestSortBySalary(){// Creating an empty ArrayList of Recruitment typeArrayList<Recruitment>list=newArrayList<Recruitment>();addListElements(list);// Sorting the entries by company nameCollections.sort(list,newGetDataBySalary());// Assert the data for sorting by company nameassertEquals(true,list.get(0).getCompanyName().equalsIgnoreCase("abc consultants"));assertEquals(true,list.get(1).getCompanyName().equalsIgnoreCase("nj consultants"));assertEquals(true,list.get(2).getSalary()==80000.0);assertTrue(list.get(0).getEmployeeName().equalsIgnoreCase("Phoebe"));assertTrue(list.get(list.size()-1).getCompanyName().equalsIgnoreCase("xyz consultants"));assertTrue(list.get(list.size()-1).getDesignation().equalsIgnoreCase("ProgramManager"));assertTrue(list.get(0).getSalary()==70000.0);assertTrue(list.get(list.size()-1).getSalary()==200000.0);}@DisplayName("Test check whether sorting done by designation ")@TestpublicvoidtestSortByDesignation(){// Creating an empty ArrayList of Recruitment typeArrayList<Recruitment>list=newArrayList<Recruitment>();addListElements(list);// Sorting the entries by DesignationCollections.sort(list,newGetDataByDesignation());// Assert the data for sorting by company nameassertEquals(true,list.get(0).getCompanyName().equalsIgnoreCase("abc consultants"));assertEquals(true,list.get(1).getCompanyName().equalsIgnoreCase("nyc consultants"));assertEquals(true,list.get(2).getSalary()==200000.0);assertTrue(list.get(0).getEmployeeName().equalsIgnoreCase("Rachel"));assertTrue(list.get(list.size()-1).getCompanyName().equalsIgnoreCase("chennai consultants"));assertTrue(list.get(list.size()-1).getDesignation().equalsIgnoreCase("ProjectManager"));assertTrue(list.get(0).getSalary()==100000.0);assertTrue(list.get(list.size()-1).getSalary()==150000.0);}@DisplayName("Test check whether sorting done both designation and salary ")@TestpublicvoidtestSortByBothDesignationAndSalary(){// Creating an empty ArrayList of Recruitment typeArrayList<Recruitment>list=newArrayList<Recruitment>();addListElements(list);// Sorting the entries by designation and salaryCollections.sort(list,newGetDataByDesignationAndSalary());// Assert the data for sorting by designation and salaryassertEquals(true,list.get(0).getCompanyName().equalsIgnoreCase("abc consultants"));assertEquals(true,list.get(1).getCompanyName().equalsIgnoreCase("nj consultants"));assertEquals(true,list.get(2).getSalary()==80000.0);assertTrue(list.get(0).getEmployeeName().equalsIgnoreCase("Phoebe"));assertTrue(list.get(list.size()-2).getCompanyName().equalsIgnoreCase("chennai consultants"));assertTrue(list.get(list.size()-2).getDesignation().equalsIgnoreCase("ProjectManager"));assertTrue(list.get(3).getSalary()==85000.0);assertTrue(list.get(list.size()-1).getSalary()==200000.0);assertTrue(list.get(list.size()-1).getDesignation().equalsIgnoreCase("ProgramManager"));}}

Let us see the output and from here we can understand that comparisons can be done on any field and even on more than one field.



Next Article
Practice Tags :

Similar Reads

close