Uso de Comparator para Ordenamiento Personalizado
La interfaz Comparable proporciona un orden natural para nuestra clase, pero a veces necesitamos ordenar objetos de diferentes maneras según diferentes criterios. Aquí es donde la interfaz Comparator resulta útil.
Comprensión de la Interfaz Comparator
La interfaz Comparator<T> define un método compare(T o1, T o2) que compara dos objetos del mismo tipo. A diferencia de Comparable, que es implementada por la clase que se está comparando, un Comparator es una clase separada o una expresión lambda que puede definir varios criterios de ordenamiento.
Creación de Comparadores Personalizados
Creemos varios comparadores para nuestra clase Student:
NameComparator: Ordena a los estudiantes por nombre (alfabéticamente)
AgeComparator: Ordena a los estudiantes por edad (ascendente)
GradeComparator: Ordena a los estudiantes por calificación (descendente)
Cree un nuevo archivo llamado StudentComparators.java con el siguiente contenido:
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());
}
}
}
Prueba de los Comparadores
Ahora, creemos una clase de prueba para demostrar cómo usar estos comparadores. Cree un archivo llamado 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);
}
}
}
Compilación y Ejecución
Compilemos y ejecutemos nuestro código:
javac Student.java StudentComparators.java ComparatorTest.java
java ComparatorTest
La salida demostrará cómo los diferentes comparadores afectan el orden de clasificación de los estudiantes:
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)
Diferencias Clave entre Comparable y Comparator
Comprender las diferencias entre Comparable y Comparator es importante:
-
Ubicación de la implementación:
Comparable es implementado por la propia clase.
Comparator se implementa en una clase separada o como una expresión lambda.
-
Número de ordenamientos:
Comparable define un único "orden natural" para una clase.
Comparator permite múltiples ordenamientos diferentes.
-
Firmas de los métodos:
Comparable tiene int compareTo(T o)
Comparator tiene int compare(T o1, T o2)
-
Uso:
Comparable es más simple cuando hay un orden natural obvio.
Comparator es más flexible cuando se necesitan múltiples ordenamientos.
En el paso final, crearemos una aplicación del mundo real que utiliza tanto Comparable como Comparator para administrar una base de datos de estudiantes.