Apprivoiser le chaos : trier un ArrayList d'objets par n'importe quel champ en Java
\n\nImaginez : vous développez une application pour un catalogue de livres. Vous avez une classe Book avec les champs title, author, price et year. Vous empilez des dizaines de livres dans un ArrayList. Et là, l'utilisateur demande : « Montre-moi d'abord les plus récents ! », puis une minute après : « Non, trie par prix ! ».
Si vous avez déjà été confronté à ce problème, vous savez qu'appeler simplement Collections.sort(list) ne suffit pas. Java ne lit pas dans les pensées. Il faut lui expliquer exactement sur quel champ trier. C'est précisément le sujet de la question la plus populaire sur Stack Overflow concernant le tri d'objets personnalisés.
Dans cet article, nous allons passer en revue toutes les méthodes pour trier un ArrayList par propriété d'objet : du classique Comparable aux élégantes expressions lambda et au Stream API. C'est parti !
Préparation : la classe à trier
\n\nPour les exemples, créons une classe simple Person :
public class Person {\n private String name;\n private int age;\n private double salary;\n\n public Person(String name, int age, double salary) {\n this.name = name;\n this.age = age;\n this.salary = salary;\n }\n\n // Getters (on omet les setters par souci de concision)\n public String getName() { return name; }\n public int getAge() { return age; }\n public double getSalary() { return salary; }\n\n @Override\n public String toString() {\n return name + " (" + age + " ans, $" + salary + ")";\n }\n}\n\n\nEt remplissons notre liste :
\n\nArrayList<Person> people = new ArrayList<>();\npeople.add(new Person("Anna", 28, 55000));\npeople.add(new Person("Boris", 35, 72000));\npeople.add(new Person("Victor", 22, 48000));\npeople.add(new Person("Galina", 30, 62000));\n\n\nTout est prêt pour les expériences. Comment trier cette liste par âge ? Par nom ? Par salaire ? Java propose au moins trois méthodes. Commençons par la plus fondamentale.
\n\nMéthode 1 : Comparable — intégrer la logique de tri directement dans la classe
\n\nL'interface Comparable représente l'« ordre naturel » d'un objet. Si vous souhaitez que vos objets soient toujours triés par un seul champ (par exemple, l'âge), implémentez cette interface.
Modifions notre classe Person :
public class Person implements Comparable<Person> {\n // ... champs, constructeur, getters ...\n\n @Override\n public int compareTo(Person other) {\n return Integer.compare(this.age, other.age);\n }\n}\n\n\nMaintenant, le tri se fait en une ligne :
\n\nCollections.sort(people);\nSystem.out.println(people);\n// Affichage : [Victor (22 ans, $48000), Anna (28 ans, $55000), Galina (30 ans, $62000), Boris (35 ans, $72000)]\n\n\nAvantages : simplicité, pas besoin de se soucier de comparateurs externes.
\nInconvénients : vous « câblez » dans la classe une seule méthode de tri. Si demain vous devez trier par nom, il faudra modifier la classe.
D'ailleurs, pour l'ordre inverse (du plus grand au plus petit), vous pouvez utiliser Collections.reverseOrder() :
Collections.sort(people, Collections.reverseOrder());\n\n\nMéthode 2 : Comparator — flexibilité sans modifier la classe
\n\nLorsque vous devez trier de différentes manières à différents endroits du programme, Comparator vient à la rescousse. Il s'agit d'une classe séparée ou d'une classe anonyme contenant la logique de comparaison.
Classe anonyme — la méthode de grand-père
\n\nCollections.sort(people, new Comparator<Person>() {\n @Override\n public int compare(Person p1, Person p2) {\n return p1.getName().compareTo(p2.getName());\n }\n});\n\n\nCela fonctionne, mais c'est un peu lourd. Dans