⚖️ Comparer des objets en Java : Comparable et Comparator¶
Objectif¶
Comprendre comment définir et utiliser des ordres de tri personnalisés en Java à l’aide de compareTo() et Comparator.
Pourquoi comparer des objets ?¶
Certaines structures (comme TreeSet, TreeMap, ou Collections.sort()) ont besoin de savoir comment ordonner les objets.
Deux mécanismes existent :
- Comparable → comparaison naturelle, définie dans la classe elle-même.
- Comparator → comparaison extérieure, définie hors de la classe.
L’interface Comparable<T>¶
Elle permet de définir l’ordre naturel d’une classe.
Signature¶
Valeurs possibles¶
| Retour | Signification |
|---|---|
< 0 |
L’objet courant est “plus petit” que autre |
0 |
Les deux objets sont égaux |
> 0 |
L’objet courant est “plus grand” que autre |
Exemple : tri naturel avec Comparable¶
class Etudiant implements Comparable<Etudiant> {
String nom;
double moyenne;
Etudiant(String nom, double moyenne) {
this.nom = nom;
this.moyenne = moyenne;
}
@Override
public int compareTo(Etudiant autre) {
return Double.compare(this.moyenne, autre.moyenne);
}
@Override
public String toString() {
return nom + " (" + moyenne + ")";
}
}
public class Main {
public static void main(String[] args) {
List<Etudiant> liste = List.of(
new Etudiant("Alice", 82),
new Etudiant("Bob", 91),
new Etudiant("Charles", 76)
);
List<Etudiant> tri = new ArrayList<>(liste);
Collections.sort(tri); // utilise compareTo()
System.out.println(tri);
}
}
Résultat :
L’interface Comparator<T>¶
Permet de définir un ordre alternatif, sans modifier la classe.
Signature¶
Exemple : tri avec Comparator¶
class Etudiant {
String nom;
double moyenne;
Etudiant(String nom, double moyenne) { this.nom = nom; this.moyenne = moyenne; }
}
public class Main {
public static void main(String[] args) {
List<Etudiant> liste = List.of(
new Etudiant("Alice", 82),
new Etudiant("Bob", 91),
new Etudiant("Charles", 76)
);
Comparator<Etudiant> compParNom = Comparator.comparing(e -> e.nom);
Comparator<Etudiant> compParMoyenneDesc = Comparator.comparingDouble((Etudiant e) -> e.moyenne).reversed();
List<Etudiant> triNom = new ArrayList<>(liste);
triNom.sort(compParNom);
System.out.println("Tri par nom : " + triNom.stream().map(e -> e.nom).toList());
List<Etudiant> triMoy = new ArrayList<>(liste);
triMoy.sort(compParMoyenneDesc);
System.out.println("Tri par moyenne décroissante : " + triMoy.stream().map(e -> e.nom).toList());
}
}
Résultat :
Différences entre compareTo() et Comparator¶
| Caractéristique | Comparable (compareTo) |
Comparator |
|---|---|---|
| Où le définir ? | Dans la classe elle-même | À l’extérieur de la classe |
| Nombre de tris possibles | 1 seul | Illimité |
| Usage typique | Collections.sort(liste) |
Collections.sort(liste, comp) |
| Exemple | Tri naturel (alphabétique, numérique) | Tri personnalisé |
| Avantage | Simplicité, cohérence | Flexibilité, externe |
| Inconvénient | Fige l’ordre dans la classe | Nécessite un objet externe |
Chaînage de comparateurs¶
Il est possible de combiner plusieurs critères avec thenComparing() :
Comparator<Etudiant> compCombiné =
Comparator.comparingDouble((Etudiant e) -> e.moyenne).reversed()
.thenComparing(e -> e.nom);
➡️ Trie d’abord par moyenne décroissante, puis par nom alphabétique.
Utilisation avec TreeSet ou TreeMap¶
- Si la classe implémente
Comparable, le tri naturel est utilisé. - Sinon, on peut passer un
Comparatorau constructeur.
Set<Etudiant> etudiants = new TreeSet<>(compCombiné);
etudiants.addAll(List.of(
new Etudiant("Alice", 80),
new Etudiant("Bob", 80),
new Etudiant("Charles", 70)
));
➡️ Les éléments sont stockés automatiquement dans l’ordre défini.
Résumé¶
| Situation | Solution recommandée |
|---|---|
| Vous contrôlez la classe et un seul tri est logique | Implémentez Comparable |
| Vous devez trier de plusieurs façons | Créez plusieurs Comparator |
Vous utilisez TreeSet / TreeMap avec objets non comparables |
Fournissez un Comparator au constructeur |
Vous voulez trier ponctuellement une List |
list.sort(comp) ou Collections.sort(list, comp) |