且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-11-01 07:39:11

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

Kotlin's stdlib offers a number of useful helper methods for this.

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

First, you can define a comparator using the compareBy() method and pass it to the sortedWith() extension method to receive a sorted copy of the list:

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

第二,您可以使用 compareValuesBy() 辅助方法:

Second, you can let Foo implement Comparable<Foo> using the compareValuesBy() helper method:

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() 扩展方法没有参数以接收列表的排序副本:

Then you can call the sorted() extension method without parameters to receive a sorted copy of the list:

val sortedList = list.sorted()

排序方向

如果您需要对某些值进行升序排序,而对其他值进行降序排序,则stdlib也为此提供函数:

Sorting direction

If you need to sort ascending on some values and descending on other values, the stdlib also offers functions for that:

list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })

性能注意事项

compareValuesByvararg版本未内联在字节码中,这意味着将为lambda生成匿名类.但是,如果Lambda本身不捕获状态,则将使用单例实例,而不是每次都实例化Lambda.

Performance considerations

The vararg version of compareValuesBy is not inlined in the bytecode meaning anonymous classes will be generated for the lambdas. However, if the lambdas themselves don't capture state, singleton instances will be used instead of instantiating the lambdas everytime.

如评论中的 Paul Woitaschek 所述,与多个选择器进行比较将实例化vararg调用的数组每次.您无法通过提取数组来优化此方法,因为它将在每次调用时复制.另一方面,您可以做的是将逻辑提取到静态比较器实例中并重新使用它:

As noted by Paul Woitaschek in the comments, comparing with multiple selectors will instantiate an array for the vararg call everytime. You can't optimize this by extracting the array as it will be copied on every call. What you can do, on the other hand, is extract the logic into a static comparator instance and reuse it:

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)
    }
}