Comparator and Comparable

JavaJavaBeginner
Practice Now

Introduction

Comparing objects and sorting them is a common requirement in many applications. Java provides two interfaces, Comparator and Comparable, that are used to allow classes to define their own natural order based on some criteria.

Implementing the Comparable interface

To implement the Comparable interface, the class needs to define its natural ordering. This is done by overriding the compareTo() method. The compareTo() method returns a negative integer, zero, or a positive integer if the calling object is less than, equal to, or greater than the method's argument.

The following code block provides an example of a Student class implementing the Comparable interface:

class Student implements Comparable<Student> {
    private String name;
    private int gpa;
    private int regNo;

    public Student(String name, int gpa, int regNo) {
        this.name = name;
        this.gpa = gpa;
        this.regNo = regNo;
    }

    // Override the compareTo() method
    @Override
    public int compareTo(Student other) {
        return this.gpa - other.gpa;
    }

    // Getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGpa() {
        return gpa;
    }

    public void setGpa(int gpa) {
        this.gpa = gpa;
    }

    public int getRegNo() {
        return regNo;
    }

    public void setRegNo(int regNo) {
        this.regNo = regNo;
    }
}

In the compareTo() method, we define the ordering based on the gpa of the students.

Sorting objects using Collections.sort() method

With the compareTo() method in place, we can sort a list of Student objects using the Collections.sort() method. The following code block provides an example of this:

ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student("John", 3, 101));
studentList.add(new Student("Mary", 4, 102));
studentList.add(new Student("Alice", 3, 103));

Collections.sort(studentList);

for (Student s : studentList) {
    System.out.println(s.getName() + ", GPA: " + s.getGpa());
}

The Collections.sort() method sorts the Student objects in the list based on their gpa in ascending order using the compareTo() method.

Implementing the Comparator interface

In some cases, you may want to sort objects based on different criteria. In such cases, we can use the Comparator interface.

The Comparator interface provides a compare() method that takes two arguments and returns a negative integer, zero, or a positive integer if the first argument is less than, equal to, or greater than the second.

Here is an example of a Student class with a Comparator interface implementation to sort by name:

class StudentNameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getName().compareTo(s2.getName());
    }
}

Sorting objects using a Comparator

To use a Comparator to sort objects, the sort() method of the Collections class is used. The sort() method has an overloaded version that accepts an additional argument of type Comparator.

Here is an example of using the StudentNameComparator class to sort Student objects by name:

ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student("John", 3, 101));
studentList.add(new Student("Mary", 4, 102));
studentList.add(new Student("Alice", 3, 103));

StudentNameComparator nameComparator = new StudentNameComparator();
Collections.sort(studentList, nameComparator);

for (Student s : studentList) {
    System.out.println(s.getName() + ", GPA: " + s.getGpa());
}

The Collections.sort() method uses the nameComparator object to sort Student objects based on their names in alphabetical order.

Sorting objects by multiple criteria

In some cases, you may want to sort objects by multiple criteria. For example, you may want to sort Student objects first by their gpa, then by their names.

You can do this by chaining multiple comparators using the thenComparing() method of the Comparator interface.

Here's an example of a Student class with a comparator that sorts by gpa and then name:

class StudentGpaNameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        int gpaCompare = s1.getGpa() - s2.getGpa();
        if (gpaCompare != 0) {
            return gpaCompare;
        }
        return s1.getName().compareTo(s2.getName());
    }
}

The StudentGpaNameComparator class compares Student objects based on their gpa. If the gpa is the same, it breaks the tie by comparing the Student objects based on their names.

Sorting objects by multiple criteria using thenComparing()

To sort objects by multiple criteria, use the thenComparing() method of the Comparator interface. The thenComparing() method takes a Comparator object as a parameter and returns a new Comparator object that chains the previous Comparator with the new one.

Here's an example of how to sort Student objects using the StudentGpaNameComparator class:

ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student("John", 3, 101));
studentList.add(new Student("Mary", 4, 102));
studentList.add(new Student("Alice", 3, 103));
studentList.add(new Student("Joe", 4, 104));

StudentGpaNameComparator gpaNameComparator = new StudentGpaNameComparator();
Collections.sort(studentList, gpaNameComparator);

for (Student s : studentList) {
    System.out.println(s.getName() + ", GPA: " + s.getGpa());
}

The Collections.sort() method uses the gpaNameComparator object to sort Student objects based on their gpa and then according to their names.

Sorting in descending order

By default, the Collections.sort() method sorts objects in ascending order. To sort objects in descending order, use the reverseOrder() method of the Comparator interface. This method returns a Comparator object that orders objects in the reverse order of their natural order.

Here's an example of sorting Student objects by GPA in descending order:

ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student("John", 3, 101));
studentList.add(new Student("Mary", 4, 102));
studentList.add(new Student("Alice", 3, 103));
studentList.add(new Student("Joe", 4, 104));

Comparator<Student> reverseGpaComparator = Collections.reverseOrder();
Collections.sort(studentList, new StudentGpaComparator().thenComparing(new StudentNameComparator()).reversed());

for (Student s : studentList) {
    System.out.println(s.getName() + ", GPA: " + s.getGpa());
}

The reverseGpaComparator object is used to sort Student objects in descending order of their gpa. The thenComparing() method is used to sort Student objects by name as a tiebreaker. Finally, the reversed() method is used to reverse the order of the entire comparison.

Putting it all together

Now that you know how to sort objects using Comparable and Comparator, let's put everything together in a single Java class:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

class Student implements Comparable<Student> {
    private String name;
    private int gpa;
    private int regNo;

    public Student(String name, int gpa, int regNo) {
        this.name = name;
        this.gpa = gpa;
        this.regNo = regNo;
    }

    // Override compareTo() for natural ordering
    @Override
    public int compareTo(Student other) {
        return this.gpa - other.gpa;
    }

    // Getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGpa() {
        return gpa;
    }

    public void setGpa(int gpa) {
        this.gpa = gpa;
    }

    public int getRegNo() {
        return regNo;
    }

    public void setRegNo(int regNo) {
        this.regNo = regNo;
    }
}

class StudentNameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getName().compareTo(s2.getName());
    }
}

class StudentGpaComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getGpa() - s2.getGpa();
    }
}

class StudentGpaNameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        int gpaCompare = s1.getGpa() - s2.getGpa();
        if (gpaCompare != 0) {
            return gpaCompare;
        }
        return s1.getName().compareTo(s2.getName());
    }
}

public class ComparatorComparable {
    public static void main(String[] args) {
        ArrayList<Student> studentList = new ArrayList<>();
        studentList.add(new Student("John", 3, 101));
        studentList.add(new Student("Mary", 4, 102));
        studentList.add(new Student("Alice", 3, 103));
        studentList.add(new Student("Joe", 4, 104));

        // Sort by GPA using compareTo()
        Collections.sort(studentList);

        System.out.println("Sorting by GPA:");
        for (Student s : studentList) {
            System.out.println(s.getName() + ", GPA: " + s.getGpa());
        }

        // Sort by name using Comparator
        StudentNameComparator nameComparator = new StudentNameComparator();
        Collections.sort(studentList, nameComparator);

        System.out.println("\nSorting by name:");
        for (Student s : studentList) {
            System.out.println(s.getName() + ", GPA: " + s.getGpa());
        }

        // Sort by GPA and then name using Comparator chaining
        StudentGpaNameComparator gpaNameComparator = new StudentGpaNameComparator();
        Collections.sort(studentList, gpaNameComparator);

        System.out.println("\nSorting by GPA and then name:");
        for (Student s : studentList) {
            System.out.println(s.getName() + ", GPA: " + s.getGpa());
        }

        // Sort by GPA in descending order and then name in ascending order using Comparator chaining
        Comparator<Student> reverseGpaComparator = Collections.reverseOrder();
        Collections.sort(studentList, new StudentGpaComparator().thenComparing(new StudentNameComparator()).reversed());

        System.out.println("\nSorting by GPA in descending order and then name in ascending order:");
        for (Student s : studentList) {
            System.out.println(s.getName() + ", GPA: " + s.getGpa());
        }
    }
}

Run the code

To run the code samples, save the file as ComparatorComparable.java and run the following commands in your terminal:

cd ~/project
javac ComparatorComparable.java
java ComparatorComparable

You should see the output from the sorted Student objects based on the different sorting criteria.

Summary

In this lab, you learned how to use the Comparable and Comparator interfaces in Java to sort objects based on different criteria.

Specifically, you learned how to implement the Comparable interface to define natural ordering based on a class's criteria. You also learned how to use the Comparator interface to define ordering based on different criteria and chain multiple comparators.

Finally, you learned how to sort objects using the Collections.sort() method and how to sort objects in descending order using the reverseOrder() method.

Other Java Tutorials you may like