Angenommen, ich habe eine class Foo(val a: String, val b: Int, val c: Date)
und möchte eine Liste von Foo
s basierend auf allen drei Eigenschaften sortieren . Wie würde ich das machen?
Angenommen, ich habe eine class Foo(val a: String, val b: Int, val c: Date)
und möchte eine Liste von Foo
s basierend auf allen drei Eigenschaften sortieren . Wie würde ich das machen?
Antworten:
Kotlins stdlib bietet hierfür eine Reihe nützlicher Hilfsmethoden.
Zunächst können Sie einen Komparator mithilfe der compareBy()
Methode definieren und an die sortedWith()
Erweiterungsmethode übergeben, um eine sortierte Kopie der Liste zu erhalten:
val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
Zweitens können Sie die Foo
Implementierung Comparable<Foo>
mit der compareValuesBy()
Hilfsmethode durchführen lassen:
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 })
}
Anschließend können Sie die sorted()
Erweiterungsmethode ohne Parameter aufrufen , um eine sortierte Kopie der Liste zu erhalten:
val sortedList = list.sorted()
Wenn Sie nach einigen Werten aufsteigend und nach anderen Werten absteigend sortieren müssen, bietet die stdlib auch Funktionen dafür:
list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
Die vararg
Version von compareValuesBy
ist nicht im Bytecode enthalten, was bedeutet, dass anonyme Klassen für die Lambdas generiert werden. Wenn die Lambdas selbst den Status nicht erfassen, werden Singleton-Instanzen verwendet, anstatt die Lambdas jedes Mal zu instanziieren.
Wie von Paul Woitaschek in den Kommentaren festgestellt , wird beim Vergleich mit mehreren Selektoren jedes Mal ein Array für den Vararg-Aufruf instanziiert. Sie können dies nicht optimieren, indem Sie das Array extrahieren, da es bei jedem Aufruf kopiert wird. Auf der anderen Seite können Sie die Logik in eine statische Komparatorinstanz extrahieren und wiederverwenden:
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)
}
}
ANEWARRAY kotlin/jvm/functions/Function1
compareBy
mit mehreren Lambdas wird nicht bei jedem compareTo
Aufruf ein neues Array zugewiesen .
Wenn Sie in absteigender Reihenfolge sortieren möchten, können Sie die akzeptierte Antwort verwenden:
list.sortedWith(compareByDescending<Foo> { it.a }.thenByDescending { it.b }.thenByDescending { it.c })
Oder erstellen Sie eine Erweiterungsfunktion wie compareBy
:
/**
* Similar to
* public fun <T> compareBy(vararg selectors: (T) -> Comparable<*>?): Comparator<T>
*
* but in descending order.
*/
public fun <T> compareByDescending(vararg selectors: (T) -> Comparable<*>?): Comparator<T> {
require(selectors.size > 0)
return Comparator { b, a -> compareValuesByImpl(a, b, selectors) }
}
private fun <T> compareValuesByImpl(a: T, b: T, selectors: Array<out (T) -> Comparable<*>?>): Int {
for (fn in selectors) {
val v1 = fn(a)
val v2 = fn(b)
val diff = compareValues(v1, v2)
if (diff != 0) return diff
}
return 0
}
und verwenden : list.sortedWith(compareByDescending ({ it.a }, { it.b }, { it.c }))
.
Wenn Sie nach mehreren Feldern sortieren müssen und einige Felder nach absteigend und andere nach aufsteigend, können Sie Folgendes verwenden:
YOUR_MUTABLE_LIST.sortedWith(compareBy<YOUR_OBJECT> { it.PARAM_1}.thenByDescending { it.PARAM_2}.thenBy { it.PARAM_3})