web-dev-qa-db-fra.com

Comment trier sur / comparer plusieurs valeurs dans Kotlin?

Supposons que j’ai une class Foo(val a: String, val b: Int, val c: Date) et que je veuille trier une liste de Foos en fonction de ces trois propriétés. Comment pourrais-je m'y prendre?

65
Kirill Rakhman

Stdlib de Kotlin propose un certain nombre de méthodes d’aide utiles à cet égard.

Tout d'abord, vous pouvez définir un comparateur à l'aide de la méthode compareBy() et le transmettre à la méthode d'extension sortedWith() pour recevoir une copie triée. de la liste:

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

Deuxièmement, vous pouvez laisser Foo implémenter Comparable<Foo> À l'aide de la méthode d'assistance compareValuesBy() :

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

Ensuite, vous pouvez appeler la méthode d’extension sorted() sans paramètres pour recevoir une copie triée de la liste:

val sortedList = list.sorted()

Direction du tri

Si vous avez besoin de trier par ordre croissant sur certaines valeurs et par ordre décroissant, stdlib propose également des fonctions pour cela:

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

Considérations de performance

La version vararg de compareValuesBy n'est pas insérée dans le bytecode, ce qui signifie que des classes anonymes seront générées pour les lambdas. Toutefois, si les lambdas eux-mêmes ne capturent pas l'état, des instances de singleton seront utilisées au lieu d'instancier à chaque fois les lambdas.

Comme indiqué par Paul Woitaschek dans les commentaires, la comparaison avec plusieurs sélecteurs instanciera un tableau pour l'appel vararg à chaque fois. Vous ne pouvez pas optimiser cela en extrayant le tableau car il sera copié à chaque appel. Par contre, vous pouvez extraire la logique dans une instance de comparateur statique et la réutiliser:

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)
    }
}
114
Kirill Rakhman