Java Comparable Contract
`Comparable<T>` lets a class define its natural ordering through `compareTo`. A correct implementation must satisfy the comparison sign contract and should behave consistently with `equals` when practical.
A class implements `Comparable<T>` when its instances have a natural ordering. The interface defines `compareTo(T other)`, which returns a negative integer when the receiver should come before `other`, zero when they are considered equivalent for ordering, and a positive integer when it should come after `other`.
The exact magnitude of the returned value does not matter; only its sign matters. Sorting utilities such as `Collections.sort` and `List.sort` use this ordering, as do ordered collections such as `TreeSet` and `TreeMap` when no separate comparator is supplied.
The comparison should be antisymmetric in sign: `x.compareTo(y)` should have the opposite sign of `y.compareTo(x)`. It should also be transitive: if `x` comes before `y` and `y` comes before `z`, then `x` should come before `z`. A comparison returning zero should also be stable with respect to additional comparisons: if `x.compareTo(y) == 0`, then `x` and `z` should have the same comparison sign as `y` and `z`.
A natural ordering that disagrees with `equals` can produce surprising behavior in sorted collections. For example, `TreeSet` uses `compareTo` to determine whether an element is already present, so two objects that compare as zero can be treated as duplicates even when `equals` returns false. When possible, designing the natural ordering to be consistent with `equals` avoids this mismatch.
Key points
- Implement `Comparable<T>` to define a class's natural ordering.
- `compareTo` communicates ordering through its sign, not a required numeric distance.
- The comparison must be antisymmetric and transitive, and zero comparisons must behave consistently.
- `TreeSet` and `TreeMap` can use natural ordering to determine element uniqueness or key equivalence.
- Prefer a natural ordering consistent with `equals` unless there is a deliberate reason not to.
Interview questions
What is the purpose of `Comparable` in Java?
`Comparable<T>` gives a class a natural ordering by requiring a `compareTo` method. The method indicates whether the current object is less than, equal to, or greater than another object for ordering purposes. This ordering can be used by sorting APIs and ordered collections when no separate comparator is supplied.
What rules should `compareTo` obey?
The sign of `x.compareTo(y)` should be the opposite of `y.compareTo(x)`. Comparisons should also be transitive, so ordering relationships do not contradict one another. If `x.compareTo(y)` returns zero, comparisons against a third object should preserve the same ordering sign.
Why should `compareTo` usually be consistent with `equals`?
Ordered collections such as `TreeSet` can use comparison results to determine whether an element is already represented. If two objects compare as zero but are not equal according to `equals`, the collection can treat them as equivalent for its ordering purposes. This can surprise callers who expect set behavior to follow `equals`, so consistency is generally preferable.
Why is `this.age - other.age` a poor implementation of integer comparison?
Integer subtraction can overflow when the values are far apart. Overflow can change the sign of the subtraction and therefore produce the wrong ordering. `Integer.compare(this.age, other.age)` expresses the intended comparison without relying on subtraction.
What happens when a `TreeSet` receives two objects whose `compareTo` returns zero?
The `TreeSet` treats the two objects as equivalent for set ordering purposes and will not retain both as separate entries. This can happen even when their `equals` methods return false. That is one reason the natural ordering of a class should generally be consistent with `equals`.