如何基于/比较Kotlin中的多个值进行排序?

Kir*_*man 68 comparable kotlin

假设我有一个class Foo(val a: String, val b: Int, val c: Date),我想Foo根据所有三个属性对s 列表进行排序.我该怎么做?

Kir*_*man 119

Kotlin的stdlib为此提供了许多有用的辅助方法.

首先,您可以使用该compareBy()方法定义比较器并将其传递给sortedWith()扩展方法以接收列表的已排序副本:

val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
Run Code Online (Sandbox Code Playgroud)

其次,你可以让Foo实现Comparable<Foo>使用compareValuesBy()helper方法:

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 })
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以调用sorted()不带参数的扩展方法来接收列表的排序副本:

val sortedList = list.sorted()
Run Code Online (Sandbox Code Playgroud)

排序方向

如果您需要对某些值进行升序排序并降序其他值,则stdlib还提供以下功能:

list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
Run Code Online (Sandbox Code Playgroud)

性能考虑因素

vararg版本compareValuesBy中的字节代码的意思匿名类是不是内联将为lambda表达式生成.但是,如果lambdas本身不捕获状态,则将使用单例实例而不是每次实例化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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果您使用多个lambdas函数(存在一个只有一个内联的重载),则它们不会内联**.这意味着每次调用comapreTo都会创建一个新对象.为了防止您将选择器移动到伴随对象,所以选择器只分配一次.我在这里创建了一个剪辑:https://gist.github.com/PaulWoitaschek/7f3c4d5310a66ed4984785ee2d6f70ed (3认同)