Подтвердить что ты не робот

Как сортировать на основе/сравнивать несколько значений в Котлин?

Скажем, у меня есть class Foo(val a: String, val b: Int, val c: Date), и я хочу отсортировать список Foo на основе всех трех свойств. Как я могу это сделать?

4b9b3361

Ответ 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)
    }
}