MWCodebymw.de ↗
Kotlin

Nach mehreren Kriterien sortieren – sortedWith, compareBy und thenBy

Erst nach Priorität, dann nach Datum, und die ohne Termin ganz nach hinten: Solche Sortierungen werden mit einem handgeschriebenen Comparator schnell unleserlich. Kotlin hat dafür eine Baukastenschreibweise.

Eine Aufgabenliste soll so erscheinen: offene zuerst, darin die dringendste Priorität oben, bei gleicher Priorität das ältere Fälligkeitsdatum zuerst, und Aufgaben ohne Datum ganz unten. Von Hand wird daraus ein compareTo-Knäuel mit vier if. Kotlin baut den Vergleich stattdessen aus Bausteinen.

Die Grundform

val sortiert = aufgaben.sortedWith(
    compareBy<Aufgabe> { it.erledigt }          // false vor true
        .thenByDescending { it.prioritaet }     // hohe Priorität zuerst
        .thenBy(nullsLast()) { it.faellig }     // ohne Datum ganz nach hinten
        .thenBy { it.titel }                    // stabiler Endanschlag
)

Das liest sich wie die Anforderung. compareBy startet, thenBy verfeinert, thenByDescending dreht nur diesen einen Schritt um.

nullsLast und nullsFirst

val nachTermin = termine.sortedWith(compareBy(nullsLast()) { it.datum })

Ohne diese Angabe müsstest du vorher partitionieren oder mit einem Ersatzwert arbeiten – beides fehleranfällig. nullsLast() (und nullsFirst()) erzeugen einen Comparator, der die Lücken bewusst einsortiert.

Groß-/Kleinschreibung und Umlaute

// Einfach, aber ASCII-naiv: "Äpfel" landet hinter "Zwetschge"
val a = namen.sortedBy { it.lowercase() }

// Sprachlich richtig:
val collator = java.text.Collator.getInstance(java.util.Locale.GERMAN)
val b = namen.sortedWith(compareBy(collator) { it })

Für Listen, die Menschen lesen, ist der Collator der Unterschied zwischen „sortiert" und „richtig sortiert".

In-Place, absteigend, und der Blick auf die Kosten

liste.sortWith(compareBy { it.name })     // ändert eine MutableList
val top = werte.sortedDescending().take(10)

// Besser, wenn du nur die Top 10 brauchst:
val top10 = werte.asSequence().sortedDescending().take(10).toList()

sortedBy und Verwandte geben immer eine neue Liste zurück; sortBy/sortWith sortieren an Ort und Stelle. Und: Kotlins Sortierung ist stabil – Einträge mit gleichem Schlüssel behalten ihre Reihenfolge. Das ist der Grund, warum man mehrstufig auch durch nacheinander ausgeführte Sortierungen erreichen kann, es mit thenBy aber deutlicher hinschreibt.

Eigene Klassen vergleichbar machen

data class Version(val major: Int, val minor: Int, val patch: Int) : Comparable<Version> {
    override fun compareTo(other: Version) =
        compareValuesBy(this, other, { it.major }, { it.minor }, { it.patch })
}

listOf(Version(1, 2, 0), Version(1, 10, 3)).sorted()   // 1.2.0, dann 1.10.3

compareValuesBy ist die Einmal-Variante von compareBy – praktisch genau für compareTo.

Fallstrick

Ein Comparator muss konsistent sein: Wenn a vor b und b vor c kommt, muss a vor c kommen. Vergleiche mit Zufall, mit der aktuellen Uhrzeit oder mit Double.NaN verletzen das, und die Sortierung kann mit einer IllegalArgumentException abbrechen. Und noch etwas aus der Praxis: Sortiere nicht in der Zeichenfunktion einer Liste – in Compose gehört die sortierte Liste in den Zustand (oder hinter ein remember), sonst rechnest du sie bei jedem Neuzeichnen neu.

Wenn Listen in deiner App „irgendwie komisch" sortiert sind, ist die Ursache fast immer hier zu finden. Ich helfe gern: bymw.de.

Quellen

#Kotlin#Sortieren#Comparator#Collections#Listen

Du brauchst mehr als ein Snippet?

Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.

Projekt anfragen →