Open In App

Comparator in Kotlin

Last Updated : 12 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In programming contexts, as there arises a need for a new type, there is also a major task of ordering the instances of a type. To compare two instances of a type we implement Comparable interface. However, since in ordering instances they must be compared automatically and also since the order can vary according to various parameters, Kotlin provides a simple Comparator interface. This interface compares two objects of a type and arranges them in an order.

Comparable vs Comparator

  • Comparable: A type implements the Comparable interface when it defines its own natural order. For example, numbers are naturally sorted from smallest to largest, and strings alphabetically.
  • Comparator: Sometimes, we want to sort objects in different ways or under different conditions — for example, sorting by age or by name. Kotlin provides the Comparator interface for this purpose. A Comparator compares two objects of the same type and determines their order.

The compare Function

At the core of every comparator is the compare function. This function compares two instance of a type and returns zero if both are equal, a negative number if second instance is bigger otherwise returns a positive number.

abstract fun compare(a: T, b: T): Int

This function returns 0 if a and b are equal, a negative number if a comes before b and a positive number if a comes after b.

Comparator Extension Functions

1. reversed():

If we have a comparator but want to reverse the order (e.g., from ascending to descending), we can use the reversed() function.

fun <T> Comparator<T>.reversed(): Comparator<T>

It returns a new comparator with the reverse order.

2. then():

Sometimes, the first comparison may not decide the order (e.g., two people have the same first name). In such cases, we can add a second comparator using then().

infix fun <T> Comparator<T>.then(
comparator: Comparator<in T>
): Comparator<T>

This means: "First compare by A, but if A is equal, then compare by B".

Example:

Kotlin
fun main() {
    data class Player(val firstName: String, val lastName: String)

    val players = listOf(
        Player("Steve", "Waugh"),
        Player("Steve", "Smith"),
        Player("Virat", "Kohli"),
        Player("Kane", "Williamson"),
        Player("Joe", "Root")
    )

    println("Original List:")
    println(players)

    // Sort by first name
    val firstNameComparator = compareBy<Player> { it.firstName }
    val sortedByFirstName = players.sortedWith(firstNameComparator)
    println("Sorted by First Name:")
    println(sortedByFirstName)

    // Sort by first name, then last name
    val sortedByFirstThenLast = players.sortedWith(
        firstNameComparator.then(compareBy { it.lastName })
    )
    println("Sorted by First Name then Last Name:")
    println(sortedByFirstThenLast)

    // Reverse order
    val reversed = players.sortedWith(firstNameComparator.reversed())
    println("Reversed Order:")
    println(reversed)
}

Output:

Original List:
[Player(firstName=Steve, lastName=Waugh), Player(firstName=Steve, lastName=Smith), Player(firstName=Virat, lastName=Kohli), Player(firstName=Kane, lastName=Williamson), Player(firstName=Joe, lastName=Root)]
Sorted by First Name:
[Player(firstName=Joe, lastName=Root), Player(firstName=Kane, lastName=Williamson), Player(firstName=Steve, lastName=Waugh), Player(firstName=Steve, lastName=Smith), Player(firstName=Virat, lastName=Kohli)]
Sorted by First Name then Last Name:
[Player(firstName=Joe, lastName=Root), Player(firstName=Kane, lastName=Williamson), Player(firstName=Steve, lastName=Smith), Player(firstName=Steve, lastName=Waugh), Player(firstName=Virat, lastName=Kohli)]
Reversed Order:
[Player(firstName=Virat, lastName=Kohli), Player(firstName=Steve, lastName=Waugh), Player(firstName=Steve, lastName=Smith), Player(firstName=Kane, lastName=Williamson), Player(firstName=Joe, lastName=Root)]


thenBy() and thenByDescending()

These functions allow us to easily chain comparisons based on properties that implement Comparable:

  • thenBy: For ascending order.
  • thenByDescending: For descending order.
fun <T> Comparator<T>.thenBy(
selector: (T) -> Comparable<*>?
): Comparator<T>

Example:

Kotlin
fun main() {
    data class Box(val height: Int, val weight: Int)

    val boxes = listOf(
        Box(3, 25),
        Box(5, 50),
        Box(7, 95),
        Box(2, 10),
        Box(4, 10),
        Box(3, 45)
    )

    // First by height, then by weight
    val sortedByHeightThenWeight = boxes.sortedWith(
        compareBy<Box> { it.height }.thenBy { it.weight }
    )
    println("Sorted by Height then Weight:")
    println(sortedByHeightThenWeight)

    // First by weight, then by descending height
    val sortedByWeightThenHeightDesc = boxes.sortedWith(
        compareBy<Box> { it.weight }.thenByDescending { it.height }
    )
    println("Sorted by Weight then Height Descending:")
    println(sortedByWeightThenHeightDesc)

}

Output:

Sorted by Height then Weight:
[Box(height=2, weight=10), Box(height=3, weight=25), Box(height=3, weight=45), Box(height=4, weight=10), Box(height=5, weight=50), Box(height=7, weight=95)]
Sorted by Weight then Height Descending:
[Box(height=4, weight=10), Box(height=2, weight=10), Box(height=3, weight=25), Box(height=3, weight=45), Box(height=5, weight=50), Box(height=7, weight=95)]


thenComparator() and thenDescending()

These give even more control by allowing us to define custom comparison logic as a lambda expression.

  • thenComparator: Adds a custom comparison when the primary comparator returns equal.
  • thenDescending: Same as then, but reverses the order for the second comparison.
fun <T> Comparator<T>.thenComparator(
comparison: (a: T, b: T) -> Int
): Comparator<T>

Example:

Kotlin
fun main() {
    val pairs = listOf(
        "A" to 0,
        "B" to 1,
        "A" to 3,
        "G" to 345,
        "E" to 20,
        "J" to 0
    )
    
    // Sort by string, then by integer
    val sortedByStringThenInt = pairs.sortedWith(
        compareBy<Pair<String, Int>> { it.first }
            .thenComparator { a, b -> a.second.compareTo(b.second) }
    )
    println("Sorted by String then Integer:")
    println(sortedByStringThenInt)
    
    // Sort by integer, then by string in descending order
    val sortedByIntThenStringDesc = pairs.sortedWith(
        compareBy<Pair<String, Int>> { it.second }
            .thenDescending(compareBy { it.first })
    )
    println("Sorted by Integer then String Descending:")
    println(sortedByIntThenStringDesc)
}

Output:

Sorted by String then Integer:
[(A, 0), (A, 3), (B, 1), (E, 20), (G, 345), (J, 0)]
Sorted by Integer then String Descending:
[(J, 0), (A, 0), (B, 1), (A, 3), (E, 20), (G, 345)]



Article Tags :

Similar Reads