ZetCode

Java Comparable Interface

Last modified: April 13, 2025

The java.lang.Comparable interface is used to define the natural ordering of objects. It contains a single method compareTo that must be implemented by classes whose objects need to be sorted.

When a class implements Comparable, it means its instances have a natural ordering. This allows objects to be automatically sorted by methods like Collections.sort or used in sorted collections like TreeSet.

Comparable Interface Definition

The Comparable interface is simple, containing just one method to implement. The compareTo method compares the current object with another object of the same type. It returns a negative, zero, or positive integer.

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

The generic type parameter T specifies the type of objects that this object can be compared with. Typically, a class compares itself with objects of its own type.

Basic Comparable Implementation

Let's start with a simple example of implementing Comparable for a Person class. We'll compare Person objects based on their age.

Main.java
package com.zetcode;

class Person implements Comparable<Person> {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public int compareTo(Person other) {
        return this.age - other.age;
    }
    
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class Main {

    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 35)
        );
        
        Collections.sort(people);
        System.out.println("Sorted by age: " + people);
    }
}

In this example, Person implements Comparable<Person> and defines natural ordering by age. The compareTo method subtracts the other person's age from this person's age. Collections.sort uses this to sort the list.

String Comparison with Comparable

Strings in Java already implement Comparable, allowing them to be sorted alphabetically. Here's how String's natural ordering works.

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        List<String> names = Arrays.asList(
            "Zoe", "Alice", "Bob", "Charlie", "David"
        );
        
        Collections.sort(names);
        System.out.println("Sorted names: " + names);
        
        // Demonstrating compareTo directly
        System.out.println("\"Alice\".compareTo(\"Bob\"): " + 
                          "Alice".compareTo("Bob"));
    }
}

This example shows String's natural ordering. The list is sorted alphabetically. The direct compareTo call shows that "Alice" comes before "Bob" lexicographically, returning a negative number.

Comparing Multiple Fields

When comparing objects, we often need to consider multiple fields. Here's how to implement more complex comparison logic.

Main.java
package com.zetcode;

class Student implements Comparable<Student> {
    private String name;
    private int grade;
    private double gpa;
    
    public Student(String name, int grade, double gpa) {
        this.name = name;
        this.grade = grade;
        this.gpa = gpa;
    }
    
    @Override
    public int compareTo(Student other) {
        // First compare by grade
        int gradeCompare = Integer.compare(this.grade, other.grade);
        if (gradeCompare != 0) {
            return gradeCompare;
        }
        
        // If grades are equal, compare by GPA (descending)
        int gpaCompare = Double.compare(other.gpa, this.gpa);
        if (gpaCompare != 0) {
            return gpaCompare;
        }
        
        // If GPA also equal, compare by name
        return this.name.compareTo(other.name);
    }
    
    @Override
    public String toString() {
        return name + " (Grade " + grade + ", GPA " + gpa + ")";
    }
}

public class Main {

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", 10, 3.8),
            new Student("Bob", 10, 3.9),
            new Student("Charlie", 9, 4.0),
            new Student("David", 10, 3.9)
        );
        
        Collections.sort(students);
        System.out.println("Sorted students:");
        students.forEach(System.out::println);
    }
}

This Student class compares first by grade (ascending), then by GPA (descending), and finally by name (ascending). The example shows how to implement multi-field comparisons in a clear, maintainable way.

Comparable with Custom Objects

Here's another example with a Product class that implements Comparable based on price and then name.

Main.java
package com.zetcode;

class Product implements Comparable<Product> {
    private String name;
    private double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    @Override
    public int compareTo(Product other) {
        int priceCompare = Double.compare(this.price, other.price);
        if (priceCompare != 0) {
            return priceCompare;
        }
        return this.name.compareTo(other.name);
    }
    
    @Override
    public String toString() {
        return name + " ($" + price + ")";
    }
}

public class Main {

    public static void main(String[] args) {
        List<Product> products = Arrays.asList(
            new Product("Laptop", 999.99),
            new Product("Phone", 699.99),
            new Product("Tablet", 399.99),
            new Product("Headphones", 99.99),
            new Product("Mouse", 19.99)
        );
        
        Collections.sort(products);
        System.out.println("Products sorted by price:");
        products.forEach(System.out::println);
    }
}

The Product class compares first by price (ascending) and then by name (ascending). This ensures consistent ordering even when products have the same price. The example demonstrates sorting a list of products by their natural order.

Reverse Natural Ordering

Sometimes we need to sort in reverse of the natural order. Here's how to do it using Collections.reverseOrder().

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 3, 9, 4);
        
        // Natural order (ascending)
        Collections.sort(numbers);
        System.out.println("Natural order: " + numbers);
        
        // Reverse natural order (descending)
        Collections.sort(numbers, Collections.reverseOrder());
        System.out.println("Reverse order: " + numbers);
        
        // Strings in reverse alphabetical order
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
        Collections.sort(words, Collections.reverseOrder());
        System.out.println("Reverse alphabetical: " + words);
    }
}

This example shows how to sort in reverse of the natural order using Collections.reverseOrder(). It works with any Comparable type, including Integer and String. The comparator returned by reverseOrder() simply inverts the result of the natural comparison.

Comparable with Date Objects

Java's Date class implements Comparable, allowing easy sorting of dates in chronological order. Here's an example.

Main.java
package com.zetcode;

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

public class Main {
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        
        List<Date> dates = Arrays.asList(
            sdf.parse("2023-05-15"),
            sdf.parse("2023-01-10"),
            sdf.parse("2023-03-20"),
            sdf.parse("2023-11-05")
        );
        
        System.out.println("Original dates:");
        dates.forEach(date -> System.out.println(sdf.format(date)));
        
        Collections.sort(dates);
        
        System.out.println("\nSorted dates:");
        dates.forEach(date -> System.out.println(sdf.format(date)));
    }
}

This example demonstrates Date's natural ordering, which is chronological. The dates are parsed from strings, stored in a list, and then sorted using Collections.sort(). The output shows the dates in chronological order.

Comparable vs Comparator

While Comparable defines natural ordering, Comparator provides external comparison logic. Here's an example showing both approaches.

Main.java
package com.zetcode;

class Employee implements Comparable<Employee> {
    private String name;
    private int id;
    private String department;
    
    public Employee(String name, int id, String department) {
        this.name = name;
        this.id = id;
        this.department = department;
    }
    
    // Natural ordering by ID
    @Override
    public int compareTo(Employee other) {
        return Integer.compare(this.id, other.id);
    }
    
    public String getName() { return name; }
    public int getId() { return id; }
    public String getDepartment() { return department; }
    
    @Override
    public String toString() {
        return name + " (ID: " + id + ", Dept: " + department + ")";
    }
}

public class Main {

    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 103, "HR"),
            new Employee("Bob", 101, "IT"),
            new Employee("Charlie", 102, "Finance")
        );
        
        // Sort using natural order (by ID)
        Collections.sort(employees);
        System.out.println("Sorted by ID (natural order):");
        employees.forEach(System.out::println);
        
        // Sort using Comparator (by name)
        Collections.sort(employees, (e1, e2) -> 
            e1.getName().compareTo(e2.getName()));
        System.out.println("\nSorted by name (using Comparator):");
        employees.forEach(System.out::println);
    }
}

This example shows both approaches. Employee implements Comparable for natural ordering by ID. We also demonstrate sorting by name using a Comparator lambda. Comparable is for primary ordering, while Comparator provides alternative ordering options.

Source

Java Comparable Interface Documentation

This tutorial covered the Comparable interface with practical examples. Implementing Comparable enables natural ordering, making objects sortable with Collections.sort and usable in sorted collections. Remember to maintain the compareTo contract for consistent behavior.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.