Verwenden von Comparator für benutzerdefinierte Sortierung
Das Comparable-Interface bietet eine natürliche Reihenfolge für unsere Klasse, aber manchmal müssen wir Objekte auf unterschiedliche Weise basierend auf verschiedenen Kriterien sortieren. Hier kommt das Comparator-Interface ins Spiel.
Verstehen des Comparator-Interfaces
Das Comparator<T>-Interface definiert eine Methode compare(T o1, T o2), die zwei Objekte desselben Typs vergleicht. Im Gegensatz zu Comparable, das von der zu vergleichenden Klasse implementiert wird, ist ein Comparator eine separate Klasse oder ein Lambda-Ausdruck, der verschiedene Sortierkriterien definieren kann.
Erstellen benutzerdefinierter Comparatoren
Erstellen wir mehrere Comparatoren für unsere Student-Klasse:
NameComparator: Sortiert Studenten nach Namen (alphabetisch)
AgeComparator: Sortiert Studenten nach Alter (aufsteigend)
GradeComparator: Sortiert Studenten nach Note (absteigend)
Erstellen Sie eine neue Datei namens StudentComparators.java mit folgendem Inhalt:
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());
}
}
}
Testen der Comparatoren
Erstellen wir nun eine Testklasse, um zu demonstrieren, wie diese Comparatoren verwendet werden. Erstellen Sie eine Datei namens 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);
}
}
}
Kompilieren und Ausführen
Kompilieren und führen wir nun unseren Code aus:
javac Student.java StudentComparators.java ComparatorTest.java
java ComparatorTest
Die Ausgabe zeigt, wie sich die verschiedenen Comparatoren auf die Sortierreihenfolge der Studenten auswirken:
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)
Hauptunterschiede zwischen Comparable und Comparator
Das Verständnis der Unterschiede zwischen Comparable und Comparator ist wichtig:
-
Implementierungsort:
Comparable wird von der Klasse selbst implementiert.
Comparator wird in einer separaten Klasse oder als Lambda-Ausdruck implementiert.
-
Anzahl der Sortierungen:
Comparable definiert eine einzelne "natürliche Reihenfolge" für eine Klasse.
Comparator ermöglicht mehrere verschiedene Sortierungen.
-
Methodensignaturen:
Comparable hat int compareTo(T o)
Comparator hat int compare(T o1, T o2)
-
Verwendung:
Comparable ist einfacher, wenn es eine offensichtliche natürliche Reihenfolge gibt.
Comparator ist flexibler, wenn mehrere Sortierungen benötigt werden.
Im letzten Schritt erstellen wir eine reale Anwendung, die sowohl Comparable als auch Comparator zur Verwaltung einer Studentendatenbank verwendet.