skip to content

Tuples, Iterators & Ordering Contracts

The supporting cast around collections: Pair and Triple, the Iterable/Iterator protocol you implement to make your own type loopable, and the two ordering contracts. Small topics that show up as follow-ups to bigger ones.

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

explore

questions

15

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

What does Kotlin's `for (x in something)` loop actually require of `something`, and how does it relate to `Iterable` and `Iterator`?

level: juniorimportance: must knowfreq 70%

basics

~10 s

A for loop works on anything that has an iterator() function. That function returns an iterator with hasNext() and next(), which the loop calls repeatedly to walk through the elements.

open as a page

How do you create a Pair in Kotlin, and what does the 'to' infix function do?

level: juniorimportance: must knowfreq 70%

basics

~10 s

A Pair holds two values. You make one with Pair(a, b) or, more commonly, with the 'to' word, like 1 to "one". 'to' just builds a Pair of its left and right values.

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

Explain how destructuring works for Pair and Triple, and what componentN functions are.

level: middleimportance: must knowfreq 65%

basics

~10 s

You can unpack a Pair into two variables in one line: val (k, v) = pair. Kotlin does this by calling component1() and component2() behind the scenes. Triple adds component3() for a third variable.

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

Write a custom type that supports `for`-in by implementing the iterator protocol, and explain where the traversal state lives.

level: middleimportance: should knowfreq 55%

basics

~20 s

Add an operator fun iterator() that returns an object tracking a position. That object's hasNext() checks if more items remain and next() returns the current item and moves forward. The position lives in the iterator, not the collection.

open as a page

How does `MutableIterator.remove()` work, and why is it the safe way to delete elements while iterating a `MutableList`?

level: middleimportance: should knowfreq 45%

basics

~10 s

MutableIterator adds a remove() method that deletes the element you just returned with next(). Using it keeps the iterator consistent, so you can remove items during a loop without corrupting the traversal.

open as a page

What does toList() return on a Pair and a Triple, and what is its element type?

level: middleimportance: should knowfreq 45%

basics

~10 s

Calling toList() on a Pair gives a list of its two values; on a Triple, a list of its three values. The order matches first, second, (third).

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

An `Iterator` is single-use while an `Iterable` can be traversed repeatedly. How does this distinction affect API design and the relationship between `Iterable` and `Sequence`?

level: seniorimportance: should knowfreq 35%

basics

~20 s

An Iterator is consumed once — after you walk it, it's exhausted. An Iterable can hand out a fresh iterator each time, so you can loop over it again and again. So expose Iterable (re-iterable) in APIs, not a bare Iterator.

open as a page

Pair and Triple are data classes — what does that give you for equality, hashing, and copying, and what are the implications for using them as map keys?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Because Pair and Triple are data classes, two pairs with equal contents are equal and hash the same, so they work well as map keys or set elements. You can also copy() one while changing a field.

open as a page

When are Pair and Triple the right tool, and when should you prefer a named data class instead? Why does Kotlin not offer larger tuples?

level: principalimportance: should knowfreq 35%

basics

~20 s

Use Pair or Triple for quick, local, throwaway groupings. For anything returned from a public function or stored long-term, use a named data class so the fields have meaningful names. Kotlin stops at three on purpose to discourage anonymous blobs.

open as a page

What does `ListIterator` add over a plain `Iterator`, and when would you reach for it?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

ListIterator lets you walk a list both forward and backward. It adds previous(), hasPrevious(), and index queries nextIndex()/previousIndex(). The mutable version can also replace and insert elements during the walk.

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