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.
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.
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.
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.
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().
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.
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.
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
List all Java tutorials.