Скажем, у меня есть class Foo(val a: String, val b: Int, val c: Date)
, и я хочу отсортировать список Foo
на основе всех трех свойств. Как я могу это сделать?
Как сортировать на основе/сравнивать несколько значений в Котлин?
Ответ 1
Kotlin stdlib предлагает ряд полезных вспомогательных методов для этого.
Сначала вы можете определить компаратор с помощью метода compareBy()
и передать его в sortedWith()
метод расширения для получения отсортированной копии списка:
val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
Во-вторых, вы можете позволить Foo
реализовать Comparable<Foo>
с помощью вспомогательного метода 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 })
}
Затем вы можете вызвать sorted()
метод расширения без параметров для получения отсортированной копии списка:
val sortedList = list.sorted()
Направление сортировки
Если вам нужно сортировать по возрастанию по некоторым значениям и спускаться по другим значениям, stdlib также предлагает для этого функции:
list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
Показатели производительности
Версия vararg
compareValuesBy
не указана в байт-коде, что означает, что для lambdas будут созданы анонимные классы. Однако, если сами lambdas не захватывают состояние, экземпляры singleton будут использоваться вместо создания экземпляра lambdas каждый раз.
Как отмечено в комментариях Paul Woitaschek, сравнение с несколькими селекторами будет каждый раз создавать массив для вызова vararg. Вы не можете оптимизировать это, извлекая массив, поскольку он будет скопирован при каждом вызове. С другой стороны, вы можете извлечь логику в статический экземпляр компаратора и повторно использовать его:
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)
}
}