CodeGym /Courses /JAVA 25 SELF /Comparable interface: implementation, compareTo

Comparable interface: implementation, compareTo

JAVA 25 SELF
Level 29, Lesson 2
Available

1. The problem of comparing objects

Reminder: reference comparison vs. object comparison

You already know that in Java the == operator, when used with objects, compares their references — that is, whether they point to the same address in memory. Two objects with identical fields but created via new will be different for ==.

Person p1 = new Person("Alex", 20);
Person p2 = new Person("Alex", 20);

System.out.println(p1 == p2); // false — these are different objects in memory!

If we want to know whether they are equal by content, we use equals(), hashCode() … you already know this. But what if we need to determine who is “older”, “younger”, or “earlier alphabetically”? For example, to sort a list of users by age or by name.

The need to sort and search objects

Suppose we have a list of users and we want to sort them by age:

List<Person> people = new ArrayList<>();
people.add(new Person("John", 25));
people.add(new Person("Peter", 20));
people.add(new Person("Kate", 30));

// How to sort?
Collections.sort(people); // Oops! Java doesn't know how to compare Person!

The compiler will immediately complain: the Person class does not implement the Comparable interface. Java can’t read minds and doesn’t know what “greater” or “less” means for Person. To teach it that, we must explicitly describe the comparison rules.

2. The Comparable interface

Interface declaration

The Comparable interface is the standard way to tell Java: “My class is comparable, and here’s how to do it.”

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

Our old friend a.compareTo(b) returns:

  • a negative number — a is “less than” b.
  • If 0 — the objects are considered equal.
  • a positive number — a is “greater than” b.

Example: implementing compareTo for the Person class

Let’s create a Person class that can be compared by age:

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters (for the examples below)
    public String getName() { return name; }
    public int getAge() { return age; }

    // Implementation of compareTo method
    @Override
    public int compareTo(Person other) {
        // Sort by age (ascending)
        return Integer.compare(this.age, other.age);
        // Alternative: return this.age - other.age;
    }
}

Important: if you want to sort in descending order, just swap the arguments: Integer.compare(other.age, this.age).

Analogy. compareTo is like a judge at a competition: it must clearly decide who is ahead, who is behind, and who is at the same level. If all judges (the compareTo methods) judge differently — chaos ensues!

3. Using Comparable

Sorting collections with Comparable

Now that our class implements Comparable, sorting works out of the box:

List<Person> people = new ArrayList<>();
people.add(new Person("John", 25));
people.add(new Person("Peter", 20));
people.add(new Person("Kate", 30));

Collections.sort(people); // Uses compareTo!

for (Person p : people) {
    System.out.println(p.getName() + " (" + p.getAge() + ")");
}
// Peter (20)
// John (25)
// Kate (30)

The sort method on a list works similarly:

people.sort(null); // If you pass null, compareTo is used

Sorting by name

If we want to sort by name — change the implementation:

@Override
public int compareTo(Person other) {
    return this.name.compareTo(other.name);
}

Sorting by multiple fields

Sometimes you need to compare first by one field, and if equal — by another:

@Override
public int compareTo(Person other) {
    int cmp = Integer.compare(this.age, other.age);
    if (cmp != 0) return cmp;
    return this.name.compareTo(other.name);
}

4. Best practices when implementing Comparable

Honor the Comparable contract

  • If a.compareTo(b) == 0, then b.compareTo(a) must also be 0.
  • If a.compareTo(b) < 0, then b.compareTo(a) must be > 0 (and vice versa).
  • If a.compareTo(b) == 0, it is desirable that a.equals(b) be true (but this is not strictly required).

Why does this matter?
Collections (for example, TreeSet, TreeMap) and sorting methods can behave unpredictably if the contract is violated. For example, “duplicates” may appear in a collection where they shouldn’t exist.

Don't forget about equals and hashCode

If you implement compareTo, consider whether equals and hashCode are implemented correctly — especially if your class will be used in collections like HashSet or Map.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person)) return false;
    Person other = (Person) o;
    return age == other.age && Objects.equals(name, other.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

Do not use fields that can be null in compareTo without checking!

If a field can be null, use safe comparisons:

@Override
public int compareTo(Person other) {
    return Objects.compare(this.name, other.name, Comparator.nullsFirst(String::compareTo));
}

Don’t mutate fields participating in compareTo if the object is already in a sorted collection

This can cause the object to get “lost” inside the collection — for example, in TreeSet or TreeMap.

5. Evolving the training app: sorting users

Step 1: Define the class

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    // ... constructor, getters, compareTo, equals, hashCode ...
}

Step 2: Add users

List<Person> people = new ArrayList<>();
people.add(new Person("Mary", 23));
people.add(new Person("Greg", 19));
people.add(new Person("Anne", 25));

Step 3: Sort and print

Collections.sort(people);

for (Person p : people) {
    System.out.println(p.getName() + " (" + p.getAge() + ")");
}

Result:

Greg (19)
Mary (23)
Anne (25)

6. How Comparable works

┌────────────────────────────┐
│   Your class (Person)      │
├────────────────────────────┤
│ implements Comparable      │
│   ↓                        │
│ public int compareTo(T o)  │
│   ↓                        │
│ (this < o) → -1            │
│ (this == o) → 0            │
│ (this > o) → 1             │
└────────────────────────────┘
        │
        ▼
Collections.sort(list)
        │
        ▼
   Sorting works!

7. Useful details

How Collections.sort works

  • If a list contains objects that implement Comparable, sorting will use their compareTo method.
  • If it doesn’t implement it — you’ll get a compilation error.
  • For standard types (Integer, String, etc.) Comparable is already implemented.

Can we have multiple comparison strategies?

  • Within a single class — only one “natural order” via Comparable.
  • For alternative orders, use Comparator (next lecture).

Example: compareTo for strings

String a = "apple";
String b = "banana";
System.out.println(a.compareTo(b)); // a negative number because "apple" < "banana"

Table: what compareTo returns

Comparison Return value
this < o
< 0
this == o
0
this > o
> 0

8. Common mistakes when implementing Comparable

Mistake #1: Violating the compareTo contract.
If a.compareTo(b) returns 0, but b.compareTo(a) does not return 0, collections will behave strangely. For example, a TreeSet may treat the objects as different and add both.

Mistake #2: Using uninitialized (null) fields.
If the field you compare by can be null and you don’t check it — you’ll get a NullPointerException.

Mistake #3: Inconsistency between compareTo and equals.
If compareTo says that objects are equal (0) but equals says they are different (false), this will lead to bugs when working with collections.

Mistake #4: Mutating fields involved in compareTo after adding to a sorted collection.
It’s like changing your last name in your passport while you’re already standing in an alphabetic queue. The collection may “lose” your object.

Mistake #5: Returning only -1, 0, or 1.
The compareTo method can return any negative or positive number; it doesn’t have to be strictly -1 or 1. But for simplicity, -1/0/1 are often used.

1
Task
JAVA 25 SELF, level 29, lesson 2
Locked
Sorting a book catalog by multiple criteria 📖
Sorting a book catalog by multiple criteria 📖
1
Task
JAVA 25 SELF, level 29, lesson 2
Locked
Managing city uniqueness and ranking 🏙️
Managing city uniqueness and ranking 🏙️
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION