Java Objekte vergleichen: Methoden für mehrere Attribute

JavaBeginner
Jetzt üben

Einführung

Das Vergleichen von Java-Objekten basierend auf mehreren Attributen ist eine essentielle Fähigkeit für jeden Java-Entwickler. Diese Fähigkeit ermöglicht effizientes Sortieren, Filtern und Organisieren von Daten in Anwendungen. In diesem Lab lernen Sie, wie Sie verschiedene Vergleichsmechanismen in Java implementieren, um Objekte mit mehreren Eigenschaften zu handhaben.

Wir werden die grundlegenden Ansätze zum Vergleichen von Objekten in Java untersuchen, einschließlich des Überschreibens der equals()-Methode, der Implementierung des Comparable-Interfaces und der Verwendung des Comparator-Interfaces. Durch praktische Beispiele und praktische Übungen erhalten Sie ein solides Verständnis dafür, wann und wie Sie jeden Ansatz verwenden.

Erstellen einer Student-Klasse mit mehreren Attributen

In diesem ersten Schritt erstellen wir eine einfache Java-Klasse, um einen Studenten mit mehreren Attributen darzustellen. Dies dient als Grundlage für das Erlernen des Objektvergleichs in Java.

Einrichten der Projektstruktur

Beginnen wir damit, ein Verzeichnis für unser Projekt zu erstellen und dorthin zu navigieren:

mkdir -p ~/project/java-comparison
cd ~/project/java-comparison

Erstellen der Student-Klasse

Erstellen wir nun eine Student-Klasse mit mehreren Attributen wie Name, Alter und Note. Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens Student.java im Projektverzeichnis mit folgendem Inhalt:

public class Student {
    private String name;
    private int age;
    private double grade;

    // Constructor
    public Student(String name, int age, double grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    // Getters
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public double getGrade() {
        return grade;
    }

    // toString method for easy display
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", grade=" + grade + "}";
    }
}

Diese Klasse repräsentiert einen Studenten mit drei Attributen: name (ein String), age (eine Ganzzahl) und grade (ein Double). Wir haben auch einen Konstruktor, Getter für jedes Attribut und eine toString()-Methode hinzugefügt, um die Anzeige von Studenteninformationen zu erleichtern.

Testen der Student-Klasse

Erstellen wir eine Hauptklasse, um unsere Student-Klasse zu testen. Erstellen Sie eine neue Datei namens StudentTest.java mit folgendem Inhalt:

public class StudentTest {
    public static void main(String[] args) {
        // Create some student objects
        Student alice = new Student("Alice", 20, 85.5);
        Student bob = new Student("Bob", 22, 90.0);
        Student charlie = new Student("Charlie", 19, 78.3);

        // Display student information
        System.out.println("Student 1: " + alice);
        System.out.println("Student 2: " + bob);
        System.out.println("Student 3: " + charlie);

        // Compare two students using == (identity comparison)
        Student aliceCopy = alice;
        System.out.println("\nIdentity comparison (==):");
        System.out.println("alice == bob: " + (alice == bob));
        System.out.println("alice == aliceCopy: " + (alice == aliceCopy));

        // Try to compare students with built-in methods
        System.out.println("\nNote: At this point, we cannot properly compare students based on their attributes.");
        System.out.println("We will implement proper comparison in the next steps.");
    }
}

Kompilieren und Ausführen

Kompilieren und führen wir nun unseren Code aus:

javac StudentTest.java
java StudentTest

Sie sollten eine Ausgabe ähnlich dieser sehen:

Student 1: Student{name='Alice', age=20, grade=85.5}
Student 2: Student{name='Bob', age=22, grade=90.0}
Student 3: Student{name='Charlie', age=19, grade=78.3}

Identity comparison (==):
alice == bob: false
alice == aliceCopy: true

Note: At this point, we cannot properly compare students based on their attributes.
We will implement proper comparison in the next steps.

Grundlagen des Objektvergleichs verstehen

Beachten Sie in unserem Test, dass wir den Operator == verwendet haben, um zwei Student-Objekte zu vergleichen. Dies wird in Java als Identitätsvergleich (identity comparison) bezeichnet, der prüft, ob zwei Referenzen auf dasselbe Objekt im Speicher zeigen.

Oft müssen wir jedoch Objekte basierend auf ihrem Inhalt oder ihren Attributen vergleichen, was als Gleichheitsvergleich (equality comparison) bezeichnet wird. Java bietet hierfür verschiedene Mechanismen:

  1. Überschreiben der equals()-Methode
  2. Implementieren des Comparable-Interfaces
  3. Verwenden des Comparator-Interfaces

In den nächsten Schritten werden wir diese Mechanismen implementieren, um Student-Objekte basierend auf ihren Attributen zu vergleichen.

Implementieren der equals() und hashCode() Methoden

Bevor wir uns mit den Comparable- und Comparator-Interfaces befassen, implementieren wir zunächst die equals()- und hashCode()-Methoden in unserer Student-Klasse. Diese Methoden sind grundlegend für den korrekten Objektvergleich in Java.

Verstehen von equals() und hashCode()

In Java wird die equals()-Methode verwendet, um zu überprüfen, ob zwei Objekte basierend auf ihrem Inhalt gleich sind, während die hashCode()-Methode einen numerischen Wert generiert, der das Objekt repräsentiert. Diese beiden Methoden arbeiten zusammen, insbesondere wenn Objekte in Sammlungen wie HashMap oder HashSet gespeichert werden.

Eine korrekte Implementierung sollte folgende Regeln befolgen:

  • Wenn zwei Objekte gemäß equals() gleich sind, müssen sie denselben Hash-Code haben.
  • Wenn zwei Objekte denselben Hash-Code haben, sind sie nicht unbedingt gleich.

Aktualisieren der Student-Klasse

Aktualisieren wir nun unsere Student-Klasse, um diese Methoden zu überschreiben. Öffnen Sie Student.java und fügen Sie die folgenden Methoden hinzu:

public class Student {
    private String name;
    private int age;
    private double grade;

    // Existing constructor and getters...

    // Existing toString method...

    // Override equals method
    @Override
    public boolean equals(Object obj) {
        // Check if same object reference
        if (this == obj) return true;
        // Check if null or different class
        if (obj == null || getClass() != obj.getClass()) return false;

        // Cast to Student
        Student other = (Student) obj;

        // Compare attributes
        return age == other.age &&
               Double.compare(grade, other.grade) == 0 &&
               (name == null ? other.name == null : name.equals(other.name));
    }

    // Override hashCode method
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        result = 31 * result + (int) (Double.doubleToLongBits(grade) ^ (Double.doubleToLongBits(grade) >>> 32));
        return result;
    }
}

Die equals()-Methode folgt einem Standardmuster:

  1. Überprüfen, ob das Objekt mit sich selbst verglichen wird
  2. Überprüfen, ob das Objekt null ist oder einer anderen Klasse angehört
  3. Casten des Objekts auf den entsprechenden Typ
  4. Vergleichen jedes Attributs auf Gleichheit

Die hashCode()-Methode kombiniert die Hash-Codes aller Attribute, um einen eindeutigen Hash-Code für das Objekt zu generieren.

Testen von equals() und hashCode()

Aktualisieren wir nun unsere StudentTest.java-Datei, um diese Methoden zu testen:

public class StudentTest {
    public static void main(String[] args) {
        // Create some student objects
        Student alice = new Student("Alice", 20, 85.5);
        Student bob = new Student("Bob", 22, 90.0);
        Student aliceDuplicate = new Student("Alice", 20, 85.5); // Same attributes as Alice

        // Display student information
        System.out.println("Student 1: " + alice);
        System.out.println("Student 2: " + bob);
        System.out.println("Student 3 (Alice duplicate): " + aliceDuplicate);

        // Identity comparison (==)
        System.out.println("\nIdentity comparison (==):");
        System.out.println("alice == bob: " + (alice == bob));
        System.out.println("alice == aliceDuplicate: " + (alice == aliceDuplicate));

        // Equality comparison (equals())
        System.out.println("\nEquality comparison (equals()):");
        System.out.println("alice.equals(bob): " + alice.equals(bob));
        System.out.println("alice.equals(aliceDuplicate): " + alice.equals(aliceDuplicate));

        // Hash code comparison
        System.out.println("\nHash code comparison:");
        System.out.println("alice.hashCode(): " + alice.hashCode());
        System.out.println("bob.hashCode(): " + bob.hashCode());
        System.out.println("aliceDuplicate.hashCode(): " + aliceDuplicate.hashCode());
    }
}

Kompilieren und Ausführen

Kompilieren und führen wir nun unseren aktualisierten Code aus:

javac Student.java StudentTest.java
java StudentTest

Die Ausgabe sollte in etwa so aussehen:

Student 1: Student{name='Alice', age=20, grade=85.5}
Student 2: Student{name='Bob', age=22, grade=90.0}
Student 3 (Alice duplicate): Student{name='Alice', age=20, grade=85.5}

Identity comparison (==):
alice == bob: false
alice == aliceDuplicate: false

Equality comparison (equals()):
alice.equals(bob): false
alice.equals(aliceDuplicate): true

Hash code comparison:
alice.hashCode(): 62509338
bob.hashCode(): 62565066
aliceDuplicate.hashCode(): 62509338

Beachten Sie, dass, obwohl alice und aliceDuplicate verschiedene Objekte sind (wie durch den ==-Vergleich gezeigt), sie gemäß unserer equals()-Methode als gleich betrachtet werden, da sie die gleichen Attributwerte haben. Außerdem haben sie denselben Hash-Code, wie es der Vertrag zwischen equals() und hashCode() erfordert.

Während equals() nützlich ist, um festzustellen, ob zwei Objekte gleich sind, hilft es uns nicht, eine Reihenfolge zwischen Objekten festzulegen (z. B. welches zuerst in einer sortierten Liste stehen soll). Dafür benötigen wir das Comparable-Interface, das wir im nächsten Schritt implementieren werden.

Implementieren des Comparable-Interfaces

Die equals()-Methode ermöglicht es uns zu überprüfen, ob zwei Objekte gleich sind, aber sie hilft uns nicht, eine Reihenfolge festzulegen. Dafür stellt Java das Comparable-Interface bereit, mit dem wir eine "natürliche Reihenfolge" für unsere Objekte definieren können.

Verstehen des Comparable-Interfaces

Das Comparable<T>-Interface hat eine einzige Methode, compareTo(T o), die das aktuelle Objekt mit einem anderen Objekt desselben Typs vergleicht. Die Methode gibt Folgendes zurück:

  • Eine negative Ganzzahl, wenn das aktuelle Objekt kleiner als das andere Objekt ist
  • Null, wenn das aktuelle Objekt gleich dem anderen Objekt ist
  • Eine positive Ganzzahl, wenn das aktuelle Objekt größer als das andere Objekt ist

Durch die Implementierung dieses Interfaces können wir definieren, wie Objekte unserer Klasse sortiert werden sollen.

Aktualisieren der Student-Klasse

Aktualisieren wir nun unsere Student-Klasse, um das Comparable-Interface zu implementieren. Wir definieren die natürliche Reihenfolge basierend auf der Note des Schülers (höhere Noten zuerst), dann nach dem Alter (jüngere Schüler zuerst) und schließlich nach dem Namen (alphabetische Reihenfolge).

Aktualisieren Sie Ihre Student.java-Datei:

public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private double grade;

    // Existing constructor, getters, toString, equals, and hashCode methods...

    // Implement compareTo method from Comparable interface
    @Override
    public int compareTo(Student other) {
        // Compare by grade (descending order)
        if (Double.compare(other.grade, this.grade) != 0) {
            return Double.compare(other.grade, this.grade);
        }

        // If grades are equal, compare by age (ascending order)
        if (this.age != other.age) {
            return Integer.compare(this.age, other.age);
        }

        // If grades and ages are equal, compare by name (alphabetical order)
        return this.name.compareTo(other.name);
    }
}

Erstellen eines Tests für die Comparable-Implementierung

Erstellen wir eine neue Datei ComparableTest.java, um unsere Comparable-Implementierung zu testen:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ComparableTest {
    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 unsorted list
        System.out.println("Unsorted list of students:");
        for (Student student : students) {
            System.out.println(student);
        }

        // Sort the list using the natural ordering defined by Comparable
        Collections.sort(students);

        // Display sorted list
        System.out.println("\nSorted list of students (by grade descending, then age ascending, then name):");
        for (Student student : students) {
            System.out.println(student);
        }

        // Let's compare some students directly using compareTo
        Student alice = students.get(3); // Alice should be at index 3 after sorting
        Student bob = students.get(1);   // Bob should be at index 1 after sorting

        System.out.println("\nComparing students directly using compareTo:");
        int comparison = alice.compareTo(bob);
        System.out.println("alice.compareTo(bob) = " + comparison);

        if (comparison < 0) {
            System.out.println("Alice comes before Bob in the natural ordering");
        } else if (comparison > 0) {
            System.out.println("Bob comes before Alice in the natural ordering");
        } else {
            System.out.println("Alice and Bob are equal in the natural ordering");
        }
    }
}

Kompilieren und Ausführen

Kompilieren und führen wir nun unseren Code aus:

javac Student.java ComparableTest.java
java ComparableTest

Die Ausgabe sollte in etwa so aussehen:

Unsorted 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}

Sorted list of students (by grade descending, then age ascending, then name):
Student{name='Eve', age=20, grade=92.7}
Student{name='Bob', age=22, grade=90.0}
Student{name='Alice', age=20, grade=85.5}
Student{name='David', age=21, grade=85.5}
Student{name='Charlie', age=19, grade=78.3}

Comparing students directly using compareTo:
alice.compareTo(bob) = 1
Bob comes before Alice in the natural ordering

Beachten Sie, dass die Studenten jetzt nach ihrer Note in absteigender Reihenfolge sortiert sind. Wenn die Noten gleich sind (wie bei Alice und David, beide mit 85,5), werden sie nach dem Alter in aufsteigender Reihenfolge sortiert.

Das Comparable-Interface bietet eine natürliche Reihenfolge für unsere Objekte, aber was ist, wenn wir Objekte zu unterschiedlichen Zeiten auf unterschiedliche Weise sortieren möchten? Zum Beispiel möchten wir manchmal Studenten nach Namen und manchmal nach Alter sortieren. Hier kommt das Comparator-Interface ins Spiel, das wir im nächsten Schritt untersuchen werden.

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:

  1. NameComparator: Sortiert Studenten nach Namen (alphabetisch)
  2. AgeComparator: Sortiert Studenten nach Alter (aufsteigend)
  3. 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:

  1. Implementierungsort:

    • Comparable wird von der Klasse selbst implementiert.
    • Comparator wird in einer separaten Klasse oder als Lambda-Ausdruck implementiert.
  2. Anzahl der Sortierungen:

    • Comparable definiert eine einzelne "natürliche Reihenfolge" für eine Klasse.
    • Comparator ermöglicht mehrere verschiedene Sortierungen.
  3. Methodensignaturen:

    • Comparable hat int compareTo(T o)
    • Comparator hat int compare(T o1, T o2)
  4. 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.

Erstellen eines Studentenverwaltungssystems

Nachdem wir nun verstanden haben, wie man Objekte mit equals(), Comparable und Comparator vergleicht, erstellen wir ein einfaches Studentenverwaltungssystem, das alles in einer praktischen Anwendung zusammenführt.

Erstellen der StudentManager-Klasse

Erstellen wir eine Klasse, die eine Sammlung von Studenten verwaltet und verschiedene Operationen auf ihnen ausführt. Erstellen Sie eine Datei namens StudentManager.java:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

public class StudentManager {
    private List<Student> students;

    public StudentManager() {
        students = new ArrayList<>();
    }

    // Add a student to the list
    public void addStudent(Student student) {
        students.add(student);
    }

    // Get all students
    public List<Student> getAllStudents() {
        return new ArrayList<>(students); // Return a copy to prevent modification
    }

    // Get top students by grade
    public List<Student> getTopStudents(int count) {
        List<Student> sortedStudents = new ArrayList<>(students);
        Collections.sort(sortedStudents, new StudentComparators.GradeComparator());

        // Return the top 'count' students or all if there are fewer
        return sortedStudents.subList(0, Math.min(count, sortedStudents.size()));
    }

    // Get students sorted by name
    public List<Student> getStudentsSortedByName() {
        List<Student> sortedStudents = new ArrayList<>(students);
        Collections.sort(sortedStudents, new StudentComparators.NameComparator());
        return sortedStudents;
    }

    // Get students sorted by age
    public List<Student> getStudentsSortedByAge() {
        List<Student> sortedStudents = new ArrayList<>(students);
        Collections.sort(sortedStudents, new StudentComparators.AgeComparator());
        return sortedStudents;
    }

    // Get students with grade above threshold
    public List<Student> getStudentsAboveGrade(double threshold) {
        List<Student> result = new ArrayList<>();
        for (Student student : students) {
            if (student.getGrade() > threshold) {
                result.add(student);
            }
        }
        return result;
    }

    // Get students grouped by age
    public Map<Integer, List<Student>> getStudentsGroupedByAge() {
        Map<Integer, List<Student>> map = new HashMap<>();

        for (Student student : students) {
            int age = student.getAge();
            if (!map.containsKey(age)) {
                map.put(age, new ArrayList<>());
            }
            map.get(age).add(student);
        }

        return map;
    }

    // Find a student by name (returns null if not found)
    public Student findStudentByName(String name) {
        for (Student student : students) {
            if (student.getName().equals(name)) {
                return student;
            }
        }
        return null;
    }

    // Get student count
    public int getStudentCount() {
        return students.size();
    }
}

Erstellen der Hauptanwendung

Erstellen wir nun unsere Hauptanwendung, die die StudentManager-Klasse verwendet. Erstellen Sie eine Datei namens StudentManagementApp.java:

import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class StudentManagementApp {
    private static StudentManager manager = new StudentManager();
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        // Add some sample students
        initializeData();

        boolean running = true;
        while (running) {
            displayMenu();
            int choice = getIntInput("Enter your choice: ");

            switch (choice) {
                case 1:
                    addStudent();
                    break;
                case 2:
                    displayAllStudents();
                    break;
                case 3:
                    displayTopStudents();
                    break;
                case 4:
                    displayStudentsSortedByName();
                    break;
                case 5:
                    displayStudentsSortedByAge();
                    break;
                case 6:
                    displayStudentsAboveGrade();
                    break;
                case 7:
                    displayStudentsGroupedByAge();
                    break;
                case 8:
                    findStudent();
                    break;
                case 9:
                    running = false;
                    break;
                default:
                    System.out.println("Invalid choice. Please try again.");
            }

            System.out.println(); // Empty line for better readability
        }

        System.out.println("Thank you for using the Student Management System.");
        scanner.close();
    }

    private static void displayMenu() {
        System.out.println("===== Student Management System =====");
        System.out.println("1. Add a new student");
        System.out.println("2. Display all students");
        System.out.println("3. Display top students by grade");
        System.out.println("4. Display students sorted by name");
        System.out.println("5. Display students sorted by age");
        System.out.println("6. Display students above a grade threshold");
        System.out.println("7. Display students grouped by age");
        System.out.println("8. Find a student by name");
        System.out.println("9. Exit");
    }

    private static void initializeData() {
        manager.addStudent(new Student("Alice", 20, 85.5));
        manager.addStudent(new Student("Bob", 22, 90.0));
        manager.addStudent(new Student("Charlie", 19, 78.3));
        manager.addStudent(new Student("David", 21, 85.5));
        manager.addStudent(new Student("Eve", 20, 92.7));
    }

    private static void addStudent() {
        System.out.println("=== Add a new student ===");
        String name = getStringInput("Enter student name: ");
        int age = getIntInput("Enter student age: ");
        double grade = getDoubleInput("Enter student grade: ");

        manager.addStudent(new Student(name, age, grade));
        System.out.println("Student added successfully.");
    }

    private static void displayAllStudents() {
        System.out.println("=== All Students ===");
        List<Student> students = manager.getAllStudents();
        displayStudents(students);
    }

    private static void displayTopStudents() {
        int count = getIntInput("Enter the number of top students to display: ");
        System.out.println("=== Top " + count + " Students ===");
        List<Student> topStudents = manager.getTopStudents(count);
        displayStudents(topStudents);
    }

    private static void displayStudentsSortedByName() {
        System.out.println("=== Students Sorted by Name ===");
        List<Student> sortedStudents = manager.getStudentsSortedByName();
        displayStudents(sortedStudents);
    }

    private static void displayStudentsSortedByAge() {
        System.out.println("=== Students Sorted by Age ===");
        List<Student> sortedStudents = manager.getStudentsSortedByAge();
        displayStudents(sortedStudents);
    }

    private static void displayStudentsAboveGrade() {
        double threshold = getDoubleInput("Enter the grade threshold: ");
        System.out.println("=== Students Above Grade " + threshold + " ===");
        List<Student> filteredStudents = manager.getStudentsAboveGrade(threshold);
        displayStudents(filteredStudents);
    }

    private static void displayStudentsGroupedByAge() {
        System.out.println("=== Students Grouped by Age ===");
        Map<Integer, List<Student>> groupedStudents = manager.getStudentsGroupedByAge();

        for (Map.Entry<Integer, List<Student>> entry : groupedStudents.entrySet()) {
            System.out.println("Age " + entry.getKey() + ":");
            displayStudents(entry.getValue());
            System.out.println();
        }
    }

    private static void findStudent() {
        String name = getStringInput("Enter the name of the student to find: ");
        Student student = manager.findStudentByName(name);

        if (student != null) {
            System.out.println("=== Student Found ===");
            System.out.println(student);
        } else {
            System.out.println("Student with name '" + name + "' not found.");
        }
    }

    private static void displayStudents(List<Student> students) {
        if (students.isEmpty()) {
            System.out.println("No students to display.");
            return;
        }

        for (Student student : students) {
            System.out.println(student);
        }
    }

    private static String getStringInput(String prompt) {
        System.out.print(prompt);
        return scanner.nextLine();
    }

    private static int getIntInput(String prompt) {
        while (true) {
            try {
                System.out.print(prompt);
                String input = scanner.nextLine();
                return Integer.parseInt(input);
            } catch (NumberFormatException e) {
                System.out.println("Invalid input. Please enter a valid integer.");
            }
        }
    }

    private static double getDoubleInput(String prompt) {
        while (true) {
            try {
                System.out.print(prompt);
                String input = scanner.nextLine();
                return Double.parseDouble(input);
            } catch (NumberFormatException e) {
                System.out.println("Invalid input. Please enter a valid number.");
            }
        }
    }
}

Kompilieren und Ausführen der Anwendung

Kompilieren und führen wir nun unser Studentenverwaltungssystem aus:

javac Student.java StudentComparators.java StudentManager.java StudentManagementApp.java
java StudentManagementApp

Sie sehen nun ein interaktives Menü, mit dem Sie Folgendes tun können:

  1. Neue Studenten hinzufügen
  2. Alle Studenten anzeigen
  3. Top-Studenten nach Note anzeigen
  4. Studenten nach Namen sortiert anzeigen
  5. Studenten nach Alter sortiert anzeigen
  6. Studenten über einem Notenschwellenwert anzeigen
  7. Studenten nach Alter gruppiert anzeigen
  8. Einen Studenten nach Namen suchen
  9. Die Anwendung beenden

Probieren Sie die verschiedenen Optionen aus, um zu sehen, wie die verschiedenen Vergleichsmechanismen, die wir implementiert haben, in einer realen Anwendung funktionieren. Zum Beispiel:

  • Option 3 verwendet den GradeComparator, um Top-Studenten nach Note zu finden
  • Option 4 verwendet den NameComparator, um Studenten nach Namen zu sortieren
  • Option 5 verwendet den AgeComparator, um Studenten nach Alter zu sortieren

Dieses Studentenverwaltungssystem zeigt, wie die Vergleichsmechanismen, die wir gelernt haben, in einem realen Szenario angewendet werden können, um Daten effektiv zu verwalten und zu organisieren.

Wichtigste Erkenntnisse

Durch dieses Lab haben Sie gelernt:

  1. Wie man eine Java-Klasse mit mehreren Attributen erstellt
  2. Wie man equals() und hashCode() für einen korrekten Objektvergleich überschreibt
  3. Wie man das Comparable-Interface implementiert, um eine natürliche Reihenfolge zu definieren
  4. Wie man das Comparator-Interface für benutzerdefinierte Sortierung verwendet
  5. Wie man diese Konzepte in einer praktischen Anwendung anwendet

Diese Fähigkeiten sind für jeden Java-Entwickler grundlegend und werden in einer Vielzahl von Anwendungen nützlich sein, von der Datenverarbeitung bis zu Benutzeroberflächen.

Zusammenfassung

In diesem Lab haben Sie gelernt, wie man Java-Objekte basierend auf mehreren Attributen vergleicht. Sie haben drei wichtige Mechanismen für den Objektvergleich in Java kennengelernt:

  1. Überschreiben der Methoden equals() und hashCode(): Sie haben gelernt, wie man diese Methoden richtig implementiert, um zu überprüfen, ob zwei Objekte basierend auf ihrem Inhalt gleich sind.

  2. Implementieren des Comparable-Interfaces: Sie haben entdeckt, wie man eine natürliche Reihenfolge für Ihre Objekte definiert, indem man die compareTo()-Methode implementiert, mit der Sie Objekte basierend auf mehreren Attributen sortieren können.

  3. Verwenden des Comparator-Interfaces: Sie haben untersucht, wie man benutzerdefinierte Comparatoren erstellt, um Objekte auf unterschiedliche Weise basierend auf verschiedenen Kriterien zu sortieren.

Sie haben diese Konzepte angewendet, um ein praktisches Studentenverwaltungssystem zu erstellen, das zeigt, wie diese Vergleichsmechanismen in einer realen Anwendung zusammenarbeiten.

Diese Fähigkeiten sind grundlegend für eine effektive Java-Programmierung, insbesondere wenn Sie mit Sammlungen von Objekten arbeiten, die sortiert, gefiltert oder verglichen werden müssen. Das Verständnis des Objektvergleichs ermöglicht es Ihnen, effizientere und robustere Anwendungen zu erstellen.

Üben Sie diese Techniken weiter, da sie auf Ihrem Weg als Java-Entwickler von unschätzbarem Wert sein werden.