skip to content

Comparable & Comparator

Comparable defines a type's natural order and is what makes the comparison operators work, while Comparator supplies an external order you can build with compareBy and thenBy. Interviewers probe the compareTo contract, since an inconsistent one corrupts sorted collections.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the Comparable interface in Kotlin, and what does implementing compareTo give you?

level: juniorimportance: must knowfreq 70%

answer

  1. compareTo returns negative / zero / positive by sign
  2. operator fun -> enables < > <= >= and ..
  3. Natural order = one default order baked into the type
  4. Use compareValuesBy to avoid overflow-prone subtraction
  5. Comparable = built-in, Comparator = external

basics

~10 s

Comparable means a type has a built-in natural order. You write compareTo, which returns a negative number, zero, or positive number. Then you can sort the items and use < and > on them.

solid answer

~40 s

Comparable<T> is the standard library interface that defines a type's natural ordering via a single method, compareTo(other: T): Int. It must return a negative Int when this is less than other, 0 when equal in order, and positive when greater. Implementing it unlocks Kotlin's comparison operators (<, >, <=, >=) and the range operator (..), because the compiler desugars a < b to a.compareTo(b) < 0. It also lets you call sorted(), sortedDescending(), min(), max(), and use the value in a sorted collection like TreeSet or a sorted map. Many built-in types (Int, String, BigDecimal, LocalDate) already implement Comparable, so they sort and compare out of the box.

code

kotlin · 8 lines
kotlin
data class Money(val cents: Long) : Comparable<Money> {
    override fun compareTo(other: Money): Int = cents.compareTo(other.cents)
}

val prices = listOf(Money(500), Money(150), Money(999))
println(prices.sorted())       // ascending by cents
println(prices.max())          // Money(999)
println(Money(150) < Money(500)) // true

go deeper

for a junior

Knows Comparable means natural order and compareTo returns negative/zero/positive enabling sorting and < >.

for a middle

Explains the operator desugaring (a < b -> a.compareTo(b) < 0) and uses compareValuesBy instead of unsafe subtraction.

for a senior

Discusses the compareTo/equals consistency contract and its impact on TreeSet/TreeMap membership.

for a principal

Frames natural order as an API design choice: when a type should expose one canonical order vs leaving ordering to callers via Comparator.

## What Comparable is `Comparable<T>` is a Kotlin standard-library interface with exactly one method: ```kotlin public interface Comparable<in T> { public operator fun compareTo(other: T): Int } ``` Implementing it declares that your type has a **natural ordering** — a single, default way to rank instances. ## The compareTo contract `compareTo` returns an `Int` interpreted by **sign**, not magnitude: - **negative** -> `this` comes before `other` (this < other) - **zero** -> they are equal in ordering - **positive** -> `this` comes after `other` (this > other) The contract requires it to be: *consistent* (a < b implies b > a), *transitive* (a < b and b < c implies a < c), and ideally *consistent with equals* (compareTo == 0 should agree with `equals`). Returning `0` while `equals` says unequal is legal but causes surprises in `TreeSet`/`TreeMap`, which use compareTo, not equals, to decide membership. ## What you unlock Because `compareTo` is marked `operator`, the compiler rewrites comparison operators: - `a < b` becomes `a.compareTo(b) < 0` - `a >= b` becomes `a.compareTo(b) >= 0` - the range operator `a..b` and `in` checks on ranges also use it You also get `sorted()`, `sortedDescending()`, `min()`, `max()`, `coerceIn`, and sorted containers like `sortedSetOf` / `TreeSet`. ## Example ```kotlin data class Version(val major: Int, val minor: Int) : Comparable<Version> { override fun compareTo(other: Version): Int = compareValuesBy(this, other, { it.major }, { it.minor }) } val a = Version(1, 2) val b = Version(1, 5) println(a < b) // true -> a.compareTo(b) < 0 println(listOf(b, a).sorted()) // [Version(1,2), Version(1,5)] ``` `compareValuesBy` is a stdlib helper that compares by each selector in turn — a clean way to avoid hand-rolled subtraction (which can overflow for raw Int math). ## Comparable vs Comparator `Comparable` is the *one* natural order baked into the type. A `Comparator` is an *external*, swappable ordering you supply at the call site (e.g. `sortedWith(comparator)`). Use Comparable for the obvious default; use Comparator when you need alternative orderings.

  • Why prefer cents.compareTo(other.cents) over returning (cents - other.cents).toInt()?
    Subtraction can overflow Int/Long for far-apart values, giving the wrong sign. compareTo (or compareValuesBy) is overflow-safe.
  • Does implementing Comparable affect equals/hashCode?
    No, they are independent. But for TreeSet/TreeMap, compareTo (not equals) decides equality, so keep them consistent to avoid surprises.

Comparable is like the height marked on each person; once everyone has it, you can line them up shortest to tallest automatically.

saying these in an interview costs you the question

  • Saying compareTo returns true/false instead of an Int
  • Returning a - b and ignoring integer overflow
  • Claiming the magnitude (not the sign) of the result matters
  • Confusing Comparable (one natural order) with Comparator (external order)
  • Thinking you must override < and > directly instead of compareTo

context

open as a page

How do you build a multi-key Comparator in Kotlin using compareBy, thenBy, and thenByDescending? Show ascending-then-descending ordering.

level: middleimportance: must knowfreq 75%

basics

~10 s

Use compareBy to pick the first sort key, then chain thenBy for the next key and thenByDescending to flip a key's direction. Pass the result to sortedWith to sort by several fields in order.

open as a page

How do you handle nulls and reverse natural ordering when building Comparators in Kotlin (nullsFirst, nullsLast, reverseOrder, naturalOrder)?

level: middleimportance: should knowfreq 55%

basics

~10 s

Use naturalOrder() for the type's default order and reverseOrder() to flip it. Wrap a comparator with nullsFirst() or nullsLast() so nullable values sort to the front or back instead of crashing.

open as a page

Explain the Comparable/Comparator contract requirements (transitivity, consistency with equals) and what breaks when compareTo is inconsistent with equals in a TreeSet/TreeMap.

level: seniorimportance: should knowfreq 45%

basics

~20 s

An ordering must be consistent: if a<b and b<c then a<c, and comparisons can't contradict each other. Sorted sets and maps decide equality by compareTo, not equals, so if those disagree, elements can vanish or duplicate unexpectedly.

open as a page

How do compareBy/thenBy relate to java.util.Comparator under the hood, and how would you adapt or reuse a Comparator across Kotlin's sortedWith, maxWith, and Java APIs?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

Kotlin's compareBy and thenBy actually return java.util.Comparator instances. So a comparator you build in Kotlin works directly with Java's Comparator methods and with Kotlin functions like sortedWith, maxWith, and TreeMap.

open as a page