Comment comparer des objets Java en fonction de plusieurs attributs

JavaBeginner
Pratiquer maintenant

Introduction

Comparer des objets Java en fonction de plusieurs attributs est une compétence essentielle pour tout développeur Java. Cette capacité permet un tri, un filtrage et une organisation efficaces des données dans les applications. Dans ce lab, vous apprendrez à implémenter différents mécanismes de comparaison en Java pour gérer des objets avec plusieurs propriétés.

Nous explorerons les approches fondamentales pour comparer des objets en Java, notamment la surcharge de la méthode equals(), l'implémentation de l'interface Comparable et l'utilisation de l'interface Comparator. Grâce à des exemples pratiques et des exercices pratiques, vous acquerrez une solide compréhension de quand et comment utiliser chaque approche.

Création d'une classe Étudiant avec plusieurs attributs

Dans cette première étape, nous allons créer une classe Java simple pour représenter un étudiant avec plusieurs attributs. Cela servira de base pour l'apprentissage de la comparaison d'objets en Java.

Configuration de la structure du projet

Commençons par créer un répertoire pour notre projet et y naviguer :

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

Création de la classe Étudiant

Maintenant, créons une classe Student avec plusieurs attributs tels que le nom, l'âge et la note. Ouvrez le WebIDE et créez un nouveau fichier nommé Student.java dans le répertoire du projet avec le contenu suivant :

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 + "}";
    }
}

Cette classe représente un étudiant avec trois attributs : name (une chaîne de caractères), age (un entier) et grade (un double). Nous avons également inclus un constructeur, des getters pour chaque attribut et une méthode toString() pour faciliter l'affichage des informations sur l'étudiant.

Test de la classe Étudiant

Créons une classe principale pour tester notre classe Student. Créez un nouveau fichier nommé StudentTest.java avec le contenu suivant :

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.");
    }
}

Compilation et exécution

Maintenant, compilons et exécutons notre code :

javac StudentTest.java
java StudentTest

Vous devriez voir une sortie similaire à celle-ci :

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.

Comprendre les bases de la comparaison d'objets

Remarquez dans notre test que nous avons utilisé l'opérateur == pour comparer deux objets Student. C'est ce qu'on appelle la comparaison d'identité en Java, qui vérifie si deux références pointent vers le même objet en mémoire.

Cependant, nous avons souvent besoin de comparer des objets en fonction de leur contenu ou de leurs attributs, ce qu'on appelle la comparaison d'égalité. Java fournit plusieurs mécanismes à cet effet :

  1. Surcharge de la méthode equals()
  2. Implémentation de l'interface Comparable
  3. Utilisation de l'interface Comparator

Dans les prochaines étapes, nous implémenterons ces mécanismes pour comparer les objets Student en fonction de leurs attributs.

Implémentation des méthodes equals() et hashCode()

Avant de plonger dans les interfaces Comparable et Comparator, implémentons d'abord les méthodes equals() et hashCode() dans notre classe Student. Ces méthodes sont fondamentales pour une comparaison d'objets correcte en Java.

Comprendre equals() et hashCode()

En Java, la méthode equals() est utilisée pour vérifier si deux objets sont égaux en fonction de leur contenu, tandis que la méthode hashCode() génère une valeur numérique qui représente l'objet. Ces deux méthodes fonctionnent ensemble, en particulier lorsque les objets sont stockés dans des collections comme HashMap ou HashSet.

Une implémentation correcte doit suivre ces règles :

  • Si deux objets sont égaux selon equals(), ils doivent avoir le même code de hachage.
  • Si deux objets ont le même code de hachage, ils ne sont pas nécessairement égaux.

Mise à jour de la classe Étudiant

Mettons à jour notre classe Student pour surcharger ces méthodes. Ouvrez Student.java et ajoutez les méthodes suivantes :

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

La méthode equals() suit un modèle standard :

  1. Vérifier si l'objet est comparé à lui-même
  2. Vérifier si l'objet est nul ou d'une classe différente
  3. Convertir l'objet au type approprié
  4. Comparer chaque attribut pour l'égalité

La méthode hashCode() combine les codes de hachage de tous les attributs pour générer un code de hachage unique pour l'objet.

Test de equals() et hashCode()

Maintenant, mettons à jour notre fichier StudentTest.java pour tester ces méthodes :

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());
    }
}

Compilation et exécution

Compilons et exécutons notre code mis à jour :

javac Student.java StudentTest.java
java StudentTest

La sortie devrait ressembler à ceci :

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

Remarquez que bien qu'alice et aliceDuplicate soient des objets différents (comme le montre la comparaison ==), ils sont considérés comme égaux selon notre méthode equals() car ils ont les mêmes valeurs d'attribut. De plus, ils ont le même code de hachage, comme l'exige le contrat entre equals() et hashCode().

Bien que equals() soit utile pour déterminer si deux objets sont égaux, il ne nous aide pas à établir un ordre entre les objets (comme lequel doit venir en premier dans une liste triée). Pour cela, nous avons besoin de l'interface Comparable, que nous implémenterons à l'étape suivante.

Implémentation de l'interface Comparable

La méthode equals() nous permet de vérifier si deux objets sont égaux, mais elle ne nous aide pas à établir un ordre. Pour cela, Java fournit l'interface Comparable, qui nous permet de définir un "ordre naturel" pour nos objets.

Comprendre l'interface Comparable

L'interface Comparable<T> possède une seule méthode, compareTo(T o), qui compare l'objet courant avec un autre objet du même type. La méthode renvoie :

  • Un entier négatif si l'objet courant est inférieur à l'autre objet
  • Zéro si l'objet courant est égal à l'autre objet
  • Un entier positif si l'objet courant est supérieur à l'autre objet

En implémentant cette interface, nous pouvons définir comment les objets de notre classe doivent être ordonnés.

Mise à jour de la classe Étudiant

Mettons à jour notre classe Student pour implémenter l'interface Comparable. Nous allons définir l'ordre naturel en fonction de la note de l'étudiant (les notes les plus élevées en premier), puis par âge (les étudiants les plus jeunes en premier), et enfin par nom (ordre alphabétique).

Mettez à jour votre fichier Student.java :

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);
    }
}

Création d'un test pour l'implémentation de Comparable

Créons un nouveau fichier ComparableTest.java pour tester notre implémentation de Comparable :

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");
        }
    }
}

Compilation et exécution

Compilons et exécutons notre code :

javac Student.java ComparableTest.java
java ComparableTest

La sortie devrait ressembler à ceci :

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

Remarquez que les étudiants sont maintenant triés par leur note par ordre décroissant. Lorsque les notes sont égales (comme pour Alice et David, tous deux avec 85,5), ils sont triés par âge par ordre croissant.

L'interface Comparable fournit un ordre naturel pour nos objets, mais que se passe-t-il si nous voulons trier les objets de différentes manières à différents moments ? Par exemple, nous pourrions parfois vouloir trier les étudiants par nom, et d'autres fois par âge. C'est là que l'interface Comparator entre en jeu, que nous explorerons à l'étape suivante.

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 :

  1. NameComparator : Trie les étudiants par nom (ordre alphabétique)
  2. AgeComparator : Trie les étudiants par âge (ordre croissant)
  3. 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 :

  1. 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.
  2. Nombre d'ordres :

    • Comparable définit un seul "ordre naturel" pour une classe.
    • Comparator permet plusieurs ordres différents.
  3. Signatures de méthode :

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

Construction d'un Système de Gestion des Étudiants

Maintenant que nous comprenons comment comparer des objets en utilisant equals(), Comparable et Comparator, construisons un système simple de gestion des étudiants qui regroupe le tout dans une application pratique.

Création de la classe StudentManager

Créons une classe qui gérera une collection d'étudiants et fournira diverses opérations sur ceux-ci. Créez un fichier nommé 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();
    }
}

Création de l'application principale

Maintenant, créons notre application principale qui utilise la classe StudentManager. Créez un fichier nommé 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.");
            }
        }
    }
}

Compilation et exécution de l'application

Compilons et exécutons notre système de gestion des étudiants :

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

Vous verrez maintenant un menu interactif qui vous permet de :

  1. Ajouter de nouveaux étudiants
  2. Afficher tous les étudiants
  3. Afficher les meilleurs étudiants par note
  4. Afficher les étudiants triés par nom
  5. Afficher les étudiants triés par âge
  6. Afficher les étudiants au-dessus d'un seuil de note
  7. Afficher les étudiants regroupés par âge
  8. Trouver un étudiant par nom
  9. Quitter l'application

Essayez les différentes options pour voir comment les différents mécanismes de comparaison que nous avons implémentés fonctionnent dans une application réelle. Par exemple :

  • L'option 3 utilise le GradeComparator pour trouver les meilleurs étudiants par note
  • L'option 4 utilise le NameComparator pour trier les étudiants par nom
  • L'option 5 utilise le AgeComparator pour trier les étudiants par âge

Ce système de gestion des étudiants démontre comment les mécanismes de comparaison que nous avons appris peuvent être appliqués dans un scénario réel pour gérer et organiser efficacement les données.

Points clés à retenir

Grâce à ce lab, vous avez appris :

  1. Comment créer une classe Java avec plusieurs attributs
  2. Comment remplacer equals() et hashCode() pour une comparaison d'objets correcte
  3. Comment implémenter l'interface Comparable pour définir un ordre naturel
  4. Comment utiliser l'interface Comparator pour un ordre personnalisé
  5. Comment appliquer ces concepts dans une application pratique

Ces compétences sont fondamentales pour tout développeur Java et seront utiles dans un large éventail d'applications, du traitement des données aux interfaces utilisateur.

Résumé

Dans ce lab, vous avez appris à comparer des objets Java en fonction de plusieurs attributs. Vous avez exploré trois mécanismes clés pour la comparaison d'objets en Java :

  1. Remplacement des méthodes equals() et hashCode() : Vous avez appris à implémenter correctement ces méthodes pour vérifier si deux objets sont égaux en fonction de leur contenu.

  2. Implémentation de l'interface Comparable : Vous avez découvert comment définir un ordre naturel pour vos objets en implémentant la méthode compareTo(), ce qui vous permet de trier les objets en fonction de plusieurs attributs.

  3. Utilisation de l'interface Comparator : Vous avez exploré comment créer des comparateurs personnalisés pour trier les objets de différentes manières en fonction de divers critères.

Vous avez appliqué ces concepts pour construire un système de gestion des étudiants pratique qui démontre comment ces mécanismes de comparaison fonctionnent ensemble dans une application réelle.

Ces compétences sont fondamentales pour une programmation Java efficace, en particulier lorsque vous travaillez avec des collections d'objets qui doivent être triés, filtrés ou comparés. Comprendre la comparaison d'objets vous permet de créer des applications plus efficaces et robustes.

Continuez à pratiquer ces techniques, car elles seront inestimables dans votre parcours en tant que développeur Java.