comparable - ¿Cómo ordenar en base/comparar múltiples valores en Kotlin?
(1)
Digamos que tengo una
class Foo(val a: String, val b: Int, val c: Date)
y quiero ordenar una lista de
Foo
s basada en las tres propiedades.
¿Cómo voy a hacer esto?
Stdlib de Kotlin ofrece varios métodos útiles para esto.
Primero, puede definir un comparador utilizando el método
compareBy()
y pasarlo al método de extensión
sortedWith()
para recibir una copia ordenada de la lista:
val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
En segundo lugar, puede dejar que
Foo
implemente
Comparable<Foo>
usando el método auxiliar
compareValuesBy()
:
class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {
override fun compareTo(other: Foo)
= compareValuesBy(this, other, { it.a }, { it.b }, { it.c })
}
Luego puede llamar al método de extensión
sorted()
sin parámetros para recibir una copia ordenada de la lista:
val sortedList = list.sorted()
Dirección de clasificación
Si necesita ordenar ascendente en algunos valores y descendente en otros valores, stdlib también ofrece funciones para eso:
list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
Consideraciones de rendimiento
La versión
vararg
de
compareValuesBy
no está
compareValuesBy
en el
compareValuesBy
de
compareValuesBy
lo que significa que se generarán clases anónimas para las lambdas.
Sin embargo, si las lambdas mismas no capturan el estado, se utilizarán instancias singleton en lugar de crear instancias de las lambdas cada vez.
Como señaló Paul Woitaschek en los comentarios, la comparación con múltiples selectores creará una instancia de la llamada vararg cada vez. No puede optimizar esto extrayendo la matriz, ya que se copiará en cada llamada. Lo que puede hacer, por otro lado, es extraer la lógica en una instancia de comparación estática y reutilizarla:
class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {
override fun compareTo(other: Foo) = comparator.compare(this, other)
companion object {
// using the method reference syntax as an alternative to lambdas
val comparator = compareBy(Foo::a, Foo::b, Foo::c)
}
}