18

This code basically sorts the array in a descending order:

val arrayList = arrayListOf(1, 5, 2)

Collections.sort(arrayList, object : Comparator<Int> {
   override fun compare(x : Int, y: Int) = y - x
})

How in the world does overriding the compare method with y - x works? How does Kotlin know what y - x means to put y before x if y < x?

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Rafael Saraiva
  • 908
  • 2
  • 11
  • 23
  • See also http://stackoverflow.com/questions/33640864/how-to-sort-based-on-compare-multiple-values-in-kotlin for example of kotlin sorting helpers in stdlib. – Vadzim Dec 17 '16 at 11:08

3 Answers3

21

This actually has nothing to do with Kotlin. It's related to the Java API's Comparator interface, and how Collections.sort uses it.

From the documentation:

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

Now let's try this out for the arguments you gave:

  • 1 - 5 = -4 (a negative integer), so 1 is less than 5.
  • 5 - 2 = 3 (a positive integer), so 5 is greater than 2.
  • etc...

Collections.sort doesn't know anything about what y - x means. It simply respects the defined contract of the Comparator interface that any implementor also needs to respect (if it wants to work).

It just so happens that y - x is an implementation that does respect that contract, because Math.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
eski
  • 7,917
  • 1
  • 23
  • 34
  • I suspected this concerned java comparator logic and not just kotlin (just added the java tag). I do remember now that i have already implemented the compareTo method (class implements comparable interface) and the logic is -1, 0, 1 so i believe i do understand it now how this works. Much obliged. – Rafael Saraiva Oct 30 '15 at 17:35
  • 1
    `y - x` is _not_ an implementation that _does respect that contract_. At least not in the general case, where integer overflow may lead to undefined behavior. (For positive numbers it works, though.) – Roland Illig Aug 20 '17 at 16:57
  • 1
    @RolandIllig Good point, it's better to use a comparison: https://stackoverflow.com/questions/2728793/java-integer-compareto-why-use-comparison-vs-subtraction – eski Aug 31 '17 at 20:01
5

Since Comparator is a SAM interface you can write this code more concise using a lambda:

Collections.sort(arrayList, {x : Int, y: Int -> y - x})

Or even

Collections.sort(arrayList) {
    x, y -> y - x
}

since the lambda is the last parameter of the sort function and the datatype of x and y can be inferred.


Taking two objects and having to define a single integer for them is an abstraction of a sort definition. You basically specify in which order those elements would be layed out if sorted.

For sorting integers it might seem like an overkill, but consider having to sort more complex objects for example instances of a class Car.

This class has a colorCode and you want to sort by that:

Collections.sort(cars) {
    car1, car2 -> car1.colorCode - car2.colorCode
}

That is how you would define an order for those objects in an abstract way.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
1

In Kotlin you can also sort elements using the kotlin collections extension functions sort, sorted, sortBy .... etc

val arrayList = arrayListOf(1, 5, 2).sorted() //1,2,5

or

val arrayList = arrayListOf(1, 5, 2).sortedDescending() //5,2,1
Fredy Mederos
  • 2,506
  • 15
  • 13