Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
265 views
in Technique[技术] by (71.8m points)

comparable - How to sort based on/compare multiple values in Kotlin?

Say I have a class Foo(val a: String, val b: Int, val c: Date) and I want to sort a list of Foos based on all three properties. How would I go about this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

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

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

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

val sortedList = list.sorted()

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

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.

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...