SyntaxStudy
Sign Up
Java Generic Interfaces: Comparable and Comparator
Java Beginner 1 min read

Generic Interfaces: Comparable and Comparator

Comparable is a generic interface that defines a natural ordering for a class. By implementing compareTo(T other), objects of your class can be compared to each other and used directly with Collections.sort(), TreeMap, TreeSet, and any other API that relies on natural ordering. The compareTo method must return a negative number, zero, or a positive number to indicate less-than, equal, or greater-than respectively. Comparator is a separate generic functional interface that defines an external ordering without modifying the class being sorted. This is useful when you need multiple different orderings for the same class, or when you cannot modify the class (e.g., third-party code). Comparator can be implemented as an anonymous class, a lambda expression, or composed using Comparator.comparing() chains with thenComparing() for multi-field sorting. The two interfaces complement each other. Comparable defines a single natural ordering built into the class, while Comparator provides flexible, external orderings. A best practice is to make compareTo consistent with equals — that is, if two objects are equal, compareTo should return zero — to avoid unexpected behaviour when using sorted collections like TreeSet.
Example
import java.util.*;

class Student implements Comparable<Student> {
    String name;
    double gpa;

    Student(String name, double gpa) {
        this.name = name;
        this.gpa  = gpa;
    }

    @Override
    public int compareTo(Student other) {
        // Natural order: ascending by name
        return this.name.compareTo(other.name);
    }

    @Override
    public String toString() {
        return name + "(" + gpa + ")";
    }
}

public class ComparableDemo {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(List.of(
            new Student("Zoe",   3.8),
            new Student("Alice", 3.5),
            new Student("Bob",   3.9),
            new Student("Carol", 3.5)
        ));

        // Natural ordering (Comparable)
        Collections.sort(students);
        System.out.println("By name: " + students);

        // Comparator: descending GPA, then name
        Comparator<Student> byGpaDesc = Comparator
            .comparingDouble(Student::gpa)
            .reversed()
            .thenComparing(s -> s.name);

        students.sort(byGpaDesc);
        System.out.println("By GPA desc: " + students);

        // TreeSet uses natural order
        TreeSet<Student> tree = new TreeSet<>(students);
        System.out.println("TreeSet first: " + tree.first());
    }
}