Utilisation de Comparator pour un Ordre Personnalisé
L'interface Comparable fournit un ordre naturel pour notre classe, mais parfois nous avons besoin de trier les objets de différentes manières en fonction de différents critères. C'est là que l'interface Comparator s'avère utile.
Comprendre l'interface Comparator
L'interface Comparator<T> définit une méthode compare(T o1, T o2) qui compare deux objets du même type. Contrairement à Comparable, qui est implémentée par la classe en cours de comparaison, un Comparator est une classe séparée ou une expression lambda qui peut définir divers critères d'ordre.
Création de Comparateurs Personnalisés
Créons plusieurs comparateurs pour notre classe Student :
NameComparator : Trie les étudiants par nom (ordre alphabétique)
AgeComparator : Trie les étudiants par âge (ordre croissant)
GradeComparator : Trie les étudiants par note (ordre décroissant)
Créez un nouveau fichier appelé StudentComparators.java avec le contenu suivant :
import java.util.Comparator;
public class StudentComparators {
// Comparator for sorting students by name
public static class NameComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
return s1.getName().compareTo(s2.getName());
}
}
// Comparator for sorting students by age
public static class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
return Integer.compare(s1.getAge(), s2.getAge());
}
}
// Comparator for sorting students by grade (descending)
public static class GradeComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
return Double.compare(s2.getGrade(), s1.getGrade());
}
}
// Multi-attribute comparator: sort by grade (descending), then by name (alphabetically)
public static class GradeNameComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// First compare by grade (descending)
int gradeComparison = Double.compare(s2.getGrade(), s1.getGrade());
if (gradeComparison != 0) {
return gradeComparison;
}
// If grades are equal, compare by name (alphabetically)
return s1.getName().compareTo(s2.getName());
}
}
}
Test des Comparateurs
Maintenant, créons une classe de test pour démontrer comment utiliser ces comparateurs. Créez un fichier nommé ComparatorTest.java :
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Comparator;
public class ComparatorTest {
public static void main(String[] args) {
// Create a list of students
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20, 85.5));
students.add(new Student("Bob", 22, 90.0));
students.add(new Student("Charlie", 19, 78.3));
students.add(new Student("David", 21, 85.5));
students.add(new Student("Eve", 20, 92.7));
// Display original list
System.out.println("Original list of students:");
printStudents(students);
// Sort by name using NameComparator
Collections.sort(students, new StudentComparators.NameComparator());
System.out.println("\nStudents sorted by name:");
printStudents(students);
// Sort by age using AgeComparator
Collections.sort(students, new StudentComparators.AgeComparator());
System.out.println("\nStudents sorted by age (ascending):");
printStudents(students);
// Sort by grade using GradeComparator
Collections.sort(students, new StudentComparators.GradeComparator());
System.out.println("\nStudents sorted by grade (descending):");
printStudents(students);
// Sort by grade, then by name using GradeNameComparator
Collections.sort(students, new StudentComparators.GradeNameComparator());
System.out.println("\nStudents sorted by grade (descending), then by name:");
printStudents(students);
// Using Java 8 lambda expressions for comparators
System.out.println("\nUsing Java 8 lambda expressions:");
// Sort by name (alphabetically) using lambda
Collections.sort(students, (s1, s2) -> s1.getName().compareTo(s2.getName()));
System.out.println("\nStudents sorted by name (using lambda):");
printStudents(students);
// Sort by age (descending) using lambda
Collections.sort(students, (s1, s2) -> Integer.compare(s2.getAge(), s1.getAge()));
System.out.println("\nStudents sorted by age (descending, using lambda):");
printStudents(students);
// Using Comparator.comparing method
System.out.println("\nUsing Comparator.comparing method:");
// Sort by name
Collections.sort(students, Comparator.comparing(Student::getName));
System.out.println("\nStudents sorted by name (using Comparator.comparing):");
printStudents(students);
// Sort by grade (descending), then by age (ascending), then by name
Collections.sort(students,
Comparator.comparing(Student::getGrade, Comparator.reverseOrder())
.thenComparing(Student::getAge)
.thenComparing(Student::getName));
System.out.println("\nStudents sorted by grade (desc), then age (asc), then name:");
printStudents(students);
}
// Helper method to print the list of students
private static void printStudents(List<Student> students) {
for (Student student : students) {
System.out.println(student);
}
}
}
Compilation et exécution
Compilons et exécutons notre code :
javac Student.java StudentComparators.java ComparatorTest.java
java ComparatorTest
La sortie démontrera comment les différents comparateurs affectent l'ordre de tri des étudiants :
Original list of students:
Student{name='Alice', age=20, grade=85.5}
Student{name='Bob', age=22, grade=90.0}
Student{name='Charlie', age=19, grade=78.3}
Student{name='David', age=21, grade=85.5}
Student{name='Eve', age=20, grade=92.7}
Students sorted by name:
Student{name='Alice', age=20, grade=85.5}
Student{name='Bob', age=22, grade=90.0}
Student{name='Charlie', age=19, grade=78.3}
Student{name='David', age=21, grade=85.5}
Student{name='Eve', age=20, grade=92.7}
... (and so on for the other sorting methods)
Différences clés entre Comparable et Comparator
Il est important de comprendre les différences entre Comparable et Comparator :
-
Emplacement de l'implémentation :
Comparable est implémenté par la classe elle-même.
Comparator est implémenté dans une classe séparée ou sous forme d'expression lambda.
-
Nombre d'ordres :
Comparable définit un seul "ordre naturel" pour une classe.
Comparator permet plusieurs ordres différents.
-
Signatures de méthode :
Comparable a int compareTo(T o)
Comparator a int compare(T o1, T o2)
-
Utilisation :
Comparable est plus simple lorsqu'il existe un ordre naturel évident.
Comparator est plus flexible lorsque plusieurs ordres sont nécessaires.
Dans la dernière étape, nous allons créer une application concrète qui utilise à la fois Comparable et Comparator pour gérer une base de données d'étudiants.