Collection 02 : Comparable et Comparator
Dans la vie, il y a deux sortes de listes : celles qui sont triées, et les autres. Aujourd'hui, tu vas découvrir comment trier une liste d'éléments en Java !
Objectifs
- Savoir trier une liste d'éléments
- Savoir implémenter l'interface
Comparable - Savoir instancier un
Comparator
Comparable
Tu désires reprendre et améliorer la gestion des menus d'un fastfood, vu dans la quête Collection 01 : List.
Souviens-toi, tu avais une classe Food :
public class Food {
// attributes
private String name;
private int calories;
public Food(String name, int calories) {
this.name = name;
this.calories = calories;
}
// getters and setters omitted for brevity...
}
Et tu avais la liste suivante :
Food burger = new Food("Big Wac", 450);
Food fries = new Food("French fries", 275);
Food cheeseburger = new Food("Cheeseburger", 300);
Food sunday = new Food("Chocolate Sunday", 175);
List<Food> lunch = new ArrayList<>();
lunch.add(burger);
lunch.add(fries);
lunch.add(cheeseburger);
lunch.add(sunday);
Tu désires maintenant trier cette liste par ordre croissant de calories : ton premier réflexe serait de développer un algorithme de tri. Cependant, il existe une méthode sort, de la classe Collections :
Collections.sort(lunch);
Pourtant, si tu essaies de compiler ce code, ça ne marchera pas :
method Collections.<Food>sort(List<Food>) is not applicable
C'est logique : comment la méthode sort pourrait-elle savoir que tu désires trier une instance de Food sur son attribut calories ?
Pour ce faire, il faut que la classe à trier implémente l'interface Comparable<T> :
public interface Comparable<T> {
int compareTo(T var1);
}
Ici
Treprésente une classe générique : si tu n'as pas encore vu ce concept, sache qu'elle représente n'importe quelle classe objet. Pour plus d'information sur le sujet, consulte l'article correspondant en ressource.
Comme tu peux le remarquer, l'interface va requérir l'implémentation d'une méthode compareTo : c'est avec elle que tu vas jouer !
public class Food implements Comparable<Food> {
@Override
public int compareTo(Food food) {
return 0;
}
// ...
}
La méthode compareTo fonctionne de la façon suivante : un objet est fourni en paramètre de la méthode et va être comparé à l'instance en cours :
-
si le retour est inférieur à zéro, l'objet sera classé après l'instance (l'objet est plus petit que l'instance)
-
si le retour est supérieur à zéro, l'objet sera classé avant l'instance (l'objet est plus grand que l'instance)
-
si le retour est égal à zéro, l'objet ne changera pas de position (l'objet est égal à l'instance)
public int compareTo(Food food) {
return food.getCalories() - this.getCalories();
}
Cette façon permet de classer les éléments de manière croissante :
$ Chocolate Sunday 175
$ French fries 275
$ Cheeseburger 300
$ Big Wac 450
Il te suffirait d'intervertir les éléments à comparer afin d'inverser l'ordre :
this.getCalories() - food.getCalories()
Comparator
Tu as vu comment ajouter un comportement de tri par défaut à un objet. Cependant, tu peux vouloir trier ces mêmes éléments d'une manière différente : par exemple, par ordre alphabétique.
Pour "surcharger" le comportement par défaut (qui est ici "trier par calories"), ta vas pouvoir utiliser une implémentation de l'interface Comparator.
new Comparator<Food>() {
@Override
public int compare(Food food1, Food food2) {
return 0;
}
}
Ici la méthode compare va fonctionner de la même façon que la méthode compareTo vue dans l'étape précédente. Dans ton cas, pour classer par ordre alphabétique, il te faudra écrire le code :
Comparator<Food> comparator = new Comparator<Food>() {
@Override
public int compare(Food food1, Food food2) {
return food1.getName().compareTo(food2.getName());
}
};
Ici la méthode
compareTode la classeStringpermet de trier par ordre alphabétique
Afin de classer la liste, tu pourras appeler une surcharge de la méthode sort de Collections, de la façon suivante :
Collections.sort(lunch, comparator);
$ Big Wac 450
$ Cheeseburger 300
$ Chocolate Sunday 175
$ French fries 275
Challenge
Endgame
La population de héros réduite de moitié, Thanos a enfin terminé son travail. Aussi lui vient-il une révélation tardive : que va-t-il faire maintenant ?
Il hésite entre devenir fermier et recenser des héros restants, afin d'effectuer des statistiques.
Thanos choisit finalement la seconde option : il programme donc son gant de l'infini afin de réaliser des actions de tri sur des listes.
Pour démarrer, fais un Fork du dépôt suivant puis clone-le en local.
Pense bien à faire un Fork, sinon tu ne pourras rien pousser !
- Modifie la classe
Heroafin d'implémenterComparable. L'ordre de tri par défaut sera sur l'attributname, par ordre croissant. - Dans la classe
Thanos, fait en sorte que la liste de héros soit bien triée comme voulu. - Dans la classe
Thanos, ajoute unComparatorpermettant de trier la liste de héros sur l'attributagedécroissant.
Voici le résultat attendu lors de l'exécution de Thanos :
$ Order by name:
$ Black Widow, 34
$ Captain America, 100
$ Hulk, 49
$ Thor, 1501
$
$ Order by age:
$ Thor, 1501
$ Captain America, 100
$ Hulk, 49
$ Black Widow, 34
Critères de validation
- Le dépôt GitHub contient bien les fichiers
Hero.javaetThanos.java - La classe
Thanosse compile sans erreur et affiche dans le terminal le même résultat que celui présenté précédemment