Java Comparable Interface
Last modified: April 13, 2025
The java.lang.Comparable
interface defines the natural ordering
of objects in Java. It provides a single method, compareTo
, which
must be implemented by any class that requires sorting. Implementing this
interface allows objects to be compared against one another based on a defined
sorting logic.
By implementing Comparable
, instances of a class gain
automatic sorting capabilities, meaning they can be arranged in order
using methods like Collections.sort
or stored in sorted collections
such as TreeSet
and TreeMap
. This ensures consistent
behavior across different sorting mechanisms.
Comparable Interface Definition
The Comparable
interface is simple, containing only one method that
determines the relative order of objects:
public interface Comparable<T> { int compareTo(T o);
The generic type parameter T
specifies the type of objects that the
implementing class can compare itself against. The compareTo
method
compares the current object with another object of the same type and returns:
- A negative integer if the current object is less than the specified object.
- Zero if both objects are equal in terms of ordering.
- A positive integer if the current object is greater than the specified object.
Usage in Sorting
Classes that implement Comparable
must define their sorting logic
inside compareTo
. For example, sorting a list of custom objects
like Person
based on age would require overriding
compareTo
as follows:
class Person implements Comparable<Person> { private int age; public Person(int age) { this.age = age; } @Override public int compareTo(Person other) { return Integer.compare(this.age, other.age); }
This ensures that sorting a collection of Person
objects will
arrange them in ascending order based on age. The Comparable
interface is widely used in scenarios requiring default sorting logic for
objects, such as sorting lists, maintaining ordered data structures, and
simplifying comparisons.
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.
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 + ")"; } } void main() { 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.
void main() { 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.
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 + ")"; } } void main() { 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.
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 + ")"; } } void main() { 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), new Product("Keyboard", 99.99), new Product("Smartwatch", 399.99), new Product("Monitor", 699.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().
void main() { 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 LocalDate Objects
Java's LocalDate
class implements Comparable
, enabling
natural chronological ordering for date values. Here's an example demonstrating
how to sort a list of dates using LocalDate
.
void main() { List<LocalDate> dates = Arrays.asList( LocalDate.parse("2023-05-15"), LocalDate.parse("2023-01-10"), LocalDate.parse("2023-03-20"), LocalDate.parse("2023-11-05") ); System.out.println("Original dates:"); dates.forEach(System.out::println); Collections.sort(dates); System.out.println("\nSorted dates:"); dates.forEach(System.out::println); }
This example demonstrates LocalDate
's natural ordering, which
follows chronological order. Dates are parsed from strings, stored in a list,
and sorted using Collections.sort
. The output displays dates in
ascending order.
Comparable vs Comparator
While Comparable
defines natural ordering, Comparator
provides external comparison logic. Here's an example showing both approaches.
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 + ")"; } } void main() { 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, Comparator.comparing(Employee::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
List all Java tutorials.