Saturday, December 28, 2024
Google search engine
HomeLanguagesJavaWhy to Use Comparator Interface Rather than Comparable Interface in Java?

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

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"?>
         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>
        <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




import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
 
public class Recruitment {
    public Recruitment(String companyName, String employeeName, String designation, double salary,boolean status) {
        super();
        this.companyName = companyName;
        this.employeeName = employeeName;
        this.designation = designation;
        this.salary = salary;
        this.status = status;
    }
 
    public Recruitment() {
        // via setter methods, rest fields are done
    }
 
    String companyName;
    String designation;
    String employeeName;
    double salary;
    boolean status;   
 
    public String getCompanyName() {
        return companyName;
    }
 
    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }
 
    public String getDesignation() {
        return designation;
    }
 
    public void setDesignation(String designation) {
        this.designation = designation;
    }
 
    public String getEmployeeName() {
        return employeeName;
    }
 
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
 
    public double getSalary() {
        return salary;
    }
 
    public void setSalary(double salary) {
        this.salary = salary;
    }
 
    public boolean isStatus() {
        return status;
    }
 
    public void setStatus(boolean status) {
        this.status = status;
    }
 
    // Method of Recruitment class
    // To print Recruitment details in main()
    public String toString()
    {
  
        // Returning attributes of Recruitment
        return this.companyName + " " + this.employeeName + " "
            + this.designation + " " + this.salary + " " + this.status;
    }
     
    public static void main(String args[]) {
        // Creating an empty ArrayList of Student type
        ArrayList<Recruitment> list = new ArrayList<Recruitment>();
  
        // Adding entries in above List
        // using add() method
        list.add(new Recruitment("abc consultants","Rachel","Analyst",100000.0,true));
        list.add(new Recruitment("nyc consultants","Monica","DBA",90000.0,true));
        list.add(new Recruitment("abc consultants","Phoebe","Programmer",70000.0,true));
        list.add(new Recruitment("nj consultants","Jane","Programmer",80000.0,false));
         
        list.add(new Recruitment("xyz consultants","Ross","ProgramManager",200000.0,true));
        list.add(new Recruitment("chennai consultants","Chandler","ProjectManager",150000.0,true));
        list.add(new Recruitment("xyz consultants","Joe","Programmer",80000.0,true));
        list.add(new Recruitment("mumbai consultants","Any","Programmer",85000.0,false));       
  
        // Display message on console for better readability
        System.out.println("Unsorted order of the details");
  
        // Iterating over entries to print them
        for (int i = 0; i < list.size(); i++)
            System.out.println(list.get(i));
  
        // Sorting the entries by company name
        Collections.sort(list, new GetDataByCompanyName());
         
        System.out.println("\nSorted by CompanyName");
  
        // We will get the sorted order now by company name
        for (int i = 0; i < list.size(); i++)
            System.out.println(list.get(i));
  
        // Sorting the  entries by salary
        Collections.sort(list, new GetDataBySalary());
 
        System.out.println("\nSorted by salary");
  
        // We will get the sorted order now by salary
        for (int i = 0; i < list.size(); i++)
            System.out.println(list.get(i));
         
         // Sorting the  entries by designation
        Collections.sort(list, new GetDataByDesignation());
 
        System.out.println("\nSorted by designation");
  
        // We will get the sorted order now by designation
        for (int i = 0; i < list.size(); i++)
            System.out.println(list.get(i));
         
        // Sorting the  entries by designation and salary
        Collections.sort(list, new GetDataByDesignationAndSalary());
  
        System.out.println("\nSorted by designation and salary");
  
        // We will get the sorted order now by salary
        for (int i = 0; i < list.size(); i++)
            System.out.println(list.get(i));
     
    }
 
}
class GetDataByCompanyName implements Comparator<Recruitment>{
 
    @Override
    public int compare(Recruitment recruitment1, Recruitment recruitment2) {
        return recruitment1.companyName.compareTo(recruitment2.companyName);
    }
 
}
class GetDataBySalary implements Comparator<Recruitment> {
    @Override
    public int compare(Recruitment recruitment1, Recruitment recruitment2) {
        return (int) (recruitment1.salary - recruitment2.salary);
    }
 
}
class GetDataByDesignation implements Comparator<Recruitment> {
    @Override
    public int compare(Recruitment recruitment1, Recruitment recruitment2) {
        return recruitment1.designation.compareTo(recruitment2.designation);
    }
 
}
 
// Using 2 fields to compare
class GetDataByDesignationAndSalary implements Comparator<Recruitment> {
 
    @Override
    public int compare(Recruitment recruitment1, Recruitment recruitment2) {
        int designationComparision = recruitment1.designation.compareTo(recruitment2.designation);
        int salaryComparision = (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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
 
import java.util.ArrayList;
import java.util.Collections;
 
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
 
public class TestComparatorServicesJava {
    private void addListElements(ArrayList<Recruitment> list) {
        // Adding entries in above List
        // using add() method
        list.add(new Recruitment("abc consultants","Rachel","Analyst",100000.0,true));
        list.add(new Recruitment("nyc consultants","Monica","DBA",90000.0,true));
        list.add(new Recruitment("abc consultants","Phoebe","Programmer",70000.0,true));
        list.add(new Recruitment("nj consultants","Jane","Programmer",80000.0,false));
         
        list.add(new Recruitment("xyz consultants","Ross","ProgramManager",200000.0,true));
        list.add(new Recruitment("chennai consultants","Chandler","ProjectManager",150000.0,true));
        list.add(new Recruitment("xyz consultants","Joe","Programmer",80000.0,true));
        list.add(new Recruitment("mumbai consultants","Any","Programmer",85000.0,false));
    }
   
    @DisplayName("Test check whether sorting done by company name ")
    @Test
    public void testSortByCompanyName() {
        // Creating an empty ArrayList of Recruitment type
        ArrayList<Recruitment> list = new ArrayList<Recruitment>();
  
        addListElements(list);
         
        // Sorting the entries by company name
        Collections.sort(list, new GetDataByCompanyName());
           
        // Assert the data for sorting by company name
        assertEquals(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 ")
    @Test
    public void testSortBySalary() {
        // Creating an empty ArrayList of Recruitment type
        ArrayList<Recruitment> list = new ArrayList<Recruitment>();
  
        addListElements(list);
         
        // Sorting the entries by company name
        Collections.sort(list, new GetDataBySalary());
           
        // Assert the data for sorting by company name
        assertEquals(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 ")
    @Test
    public void testSortByDesignation() {
        // Creating an empty ArrayList of Recruitment type
        ArrayList<Recruitment> list = new ArrayList<Recruitment>();
  
        addListElements(list);
         
        // Sorting the entries by Designation
        Collections.sort(list, new GetDataByDesignation());
           
        // Assert the data for sorting by company name
        assertEquals(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 ")
    @Test
    public void testSortByBothDesignationAndSalary() {
        // Creating an empty ArrayList of Recruitment type
        ArrayList<Recruitment> list = new ArrayList<Recruitment>();
  
        addListElements(list);
         
        // Sorting the entries by designation and salary
        Collections.sort(list, new GetDataByDesignationAndSalary());
           
        // Assert the data for sorting by designation and salary
        assertEquals(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.

 

RELATED ARTICLES

Most Popular

Recent Comments