Pourquoi un tri « simple » en Kotlin casse-t-il le code ?
\nBonjour, collègue ! Si tu as déjà programmé en Kotlin et essayé de trier une liste de tâches d'abord par date, puis par priorité, tu as sûrement rencontré une situation où le résultat ressemble à un ensemble aléatoire. Tu n'es pas seul — la question « Sort collection by multiple fields in Kotlin » a recueilli des milliers de vues sur Stack Overflow. Quoi de plus simple, apparemment ? Mais le diable, comme toujours, se cache dans les détails.
\nDans cet article, nous allons analyser les meilleures erreurs lors du tri par plusieurs champs en Kotlin, que même les développeurs expérimentés commettent. Tu découvriras comment fonctionne sortedWith, pourquoi compareBy et thenBy sont tes meilleurs amis, et comment ne pas perdre de données à cause de null. C'est parti !
Erreur n°1 : Trier comme en Java — avec un Comparator manuel
\nKotlin n'est pas Java. Mais beaucoup, venant du monde Java, écrivent :
\nval list = listOf(\n User("Alice", 25),\n User("Bob", 20),\n User("Charlie", 25)\n)\n\nval sorted = list.sortedWith(Comparator { a, b ->\n val ageCompare = a.age.compareTo(b.age)\n if (ageCompare != 0) ageCompare else a.name.compareTo(b.name)\n})\n\nOui, cela fonctionne. Mais c'est terriblement illisible, et tu peux facilement te perdre dans les chaînes de if-else. Kotlin propose une manière élégante — les fonctions de comparaison.
Comment faire correctement ?
\nval sorted = list.sortedWith(compareBy<User> { it.age }.thenBy { it.name })\n\nCourt, clair, sans logique superflue. compareBy crée un comparateur principal, et thenBy ajoute des champs secondaires. C'est le tri par plusieurs champs en Kotlin dans sa forme idéale.
Erreur n°2 : Ignorer les null lors du tri
\nSupposons que nous ayons une liste de tâches où deadline peut être null. Si tu écris simplement :
tasks.sortedBy { it.deadline }\n\n…tu obtiendras une NullPointerException. Kotlin est strict avec null, et c'est parfait. Mais que faire si null est normal ?
\nSolution : nullsFirst / nullsLast
\nUtilise des wrappers spéciaux :
\nval sorted = tasks.sortedWith(\n compareBy<Task>(nullsLast()) { it.deadline }\n .thenBy { it.priority }\n)\n\nIci, nullsLast() place toutes les dates null à la fin de la liste. Il existe aussi nullsFirst() — l'inverse. Cela résout le problème, et ton tri de collections Kotlin devient sûr.
Erreur n°3 : Confusion entre sortedBy et sortedWith
\nBeaucoup de débutants écrivent :
\nlist.sortedBy { it.age }.sortedBy { it.name }\n\nEt sont surpris que le tri par nom « écrase » le tri par âge. sortedBy crée à chaque fois une nouvelle liste et ne trie que par un seul champ. Le dernier appel est le principal.
Comment corriger ?
\nUtilise sortedWith avec une chaîne de compareBy :
list.sortedWith(compareBy({ it.age }, { it.name }))\n\nOu une version multiligne pour une logique complexe :
\nlist.sortedWith(compareBy<Person> { it.age }.thenByDescending { it.name })\n\nRappelle-toi : un seul appel à sortedWith — un seul tri par plusieurs champs.
\n\nErreur n°4 : Tri dans l'ordre inverse — au mauvais endroit
\nParfois, il faut trier par âge croissant, mais par nom décroissant. Erreur typique :
\nlist.sortedByDescending { it.age }.sortedBy { it.name }\n\nEncore le même problème — le deuxième tri casse le premier. La bonne approche :
\nval sorted = list.sortedWith(\n compareBy<Person> { it.age }\n .thenByDescending { it.name }\n)\n\nOu pour un contrôle total :
\nval sorted = list.sortedWith(\n compareBy(Person::age).thenByDescending(Person::name)\n)\n<