Как сравнивать объекты Java по нескольким атрибутам

JavaBeginner
Практиковаться сейчас

Введение

Сравнение объектов Java на основе нескольких атрибутов — важный навык для любого Java-разработчика. Эта возможность позволяет эффективно сортировать, фильтровать и организовывать данные в приложениях. В этой лабораторной работе вы узнаете, как реализовать различные механизмы сравнения в Java для работы с объектами, имеющими несколько свойств.

Мы рассмотрим основные подходы к сравнению объектов в Java, включая переопределение метода equals(), реализацию интерфейса Comparable и использование интерфейса Comparator. Благодаря практическим примерам и практическим упражнениям вы получите прочное понимание того, когда и как использовать каждый подход.

Создание класса Student с несколькими атрибутами

На этом первом шаге мы создадим простой класс Java для представления студента с несколькими атрибутами. Это послужит основой для изучения сравнения объектов в Java.

Настройка структуры проекта

Начнем с создания каталога для нашего проекта и перехода в него:

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

Создание класса Student

Теперь давайте создадим класс Student с несколькими атрибутами, такими как имя, возраст и оценка. Откройте WebIDE и создайте новый файл с именем Student.java в каталоге проекта со следующим содержимым:

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

Этот класс представляет студента с тремя атрибутами: name (String), age (целое число) и grade (double). Мы также включили конструктор, геттеры для каждого атрибута и метод toString(), чтобы упростить отображение информации о студентах.

Тестирование класса Student

Давайте создадим главный класс для тестирования нашего класса Student. Создайте новый файл с именем StudentTest.java со следующим содержимым:

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

Компиляция и запуск

Теперь давайте скомпилируем и запустим наш код:

javac StudentTest.java
java StudentTest

Вы должны увидеть вывод, похожий на этот:

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.

Основы понимания сравнения объектов

Обратите внимание, что в нашем тесте мы использовали оператор == для сравнения двух объектов Student. Это называется сравнением идентичности (identity comparison) в Java, которое проверяет, указывают ли две ссылки на один и тот же объект в памяти.

Однако нам часто нужно сравнивать объекты на основе их содержимого или атрибутов, что называется сравнением на равенство (equality comparison). Java предоставляет несколько механизмов для этой цели:

  1. Переопределение метода equals()
  2. Реализация интерфейса Comparable
  3. Использование интерфейса Comparator

На следующих шагах мы реализуем эти механизмы для сравнения объектов Student на основе их атрибутов.

Реализация методов equals() и hashCode()

Прежде чем углубляться в интерфейсы Comparable и Comparator, давайте сначала реализуем методы equals() и hashCode() в нашем классе Student. Эти методы являются фундаментальными для правильного сравнения объектов в Java.

Понимание equals() и hashCode()

В Java метод equals() используется для проверки, равны ли два объекта на основе их содержимого, в то время как метод hashCode() генерирует числовое значение, представляющее объект. Эти два метода работают вместе, особенно когда объекты хранятся в коллекциях, таких как HashMap или HashSet.

Правильная реализация должна соответствовать следующим правилам:

  • Если два объекта равны в соответствии с equals(), они должны иметь один и тот же хэш-код.
  • Если два объекта имеют один и тот же хэш-код, они не обязательно равны.

Обновление класса Student

Давайте обновим наш класс Student, чтобы переопределить эти методы. Откройте Student.java и добавьте следующие методы:

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

Метод equals() следует стандартному шаблону:

  1. Проверить, сравнивается ли объект сам с собой
  2. Проверить, является ли объект null или принадлежит другому классу
  3. Привести объект к соответствующему типу
  4. Сравнить каждый атрибут на равенство

Метод hashCode() объединяет хэш-коды всех атрибутов для генерации уникального хэш-кода для объекта.

Тестирование equals() и hashCode()

Теперь давайте обновим наш файл StudentTest.java, чтобы протестировать эти методы:

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

Компиляция и запуск

Давайте скомпилируем и запустим наш обновленный код:

javac Student.java StudentTest.java
java StudentTest

Вывод должен выглядеть примерно так:

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

Обратите внимание, что хотя alice и aliceDuplicate являются разными объектами (как показано сравнением ==), они считаются равными в соответствии с нашим методом equals(), потому что у них одинаковые значения атрибутов. Кроме того, у них одинаковый хэш-код, как того требует контракт между equals() и hashCode().

Хотя equals() полезен для определения того, равны ли два объекта, он не помогает нам установить порядок между объектами (например, какой из них должен быть первым в отсортированном списке). Для этого нам нужен интерфейс Comparable, который мы реализуем на следующем шаге.

Реализация интерфейса Comparable

Метод equals() позволяет нам проверить, равны ли два объекта, но он не помогает нам установить порядок. Для этого Java предоставляет интерфейс Comparable, который позволяет нам определить «естественный порядок» для наших объектов.

Понимание интерфейса Comparable

Интерфейс Comparable<T> имеет один метод, compareTo(T o), который сравнивает текущий объект с другим объектом того же типа. Метод возвращает:

  • Отрицательное целое число, если текущий объект меньше другого объекта
  • Ноль, если текущий объект равен другому объекту
  • Положительное целое число, если текущий объект больше другого объекта

Реализовав этот интерфейс, мы можем определить, как должны быть упорядочены объекты нашего класса.

Обновление класса Student

Давайте обновим наш класс Student, чтобы реализовать интерфейс Comparable. Мы определим естественный порядок на основе оценки студента (сначала идут более высокие оценки), затем по возрасту (сначала идут более молодые студенты) и, наконец, по имени (в алфавитном порядке).

Обновите свой файл 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);
    }
}

Создание теста для реализации Comparable

Давайте создадим новый файл ComparableTest.java, чтобы протестировать нашу реализацию 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");
        }
    }
}

Компиляция и запуск

Давайте скомпилируем и запустим наш код:

javac Student.java ComparableTest.java
java ComparableTest

Вывод должен выглядеть примерно так:

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

Обратите внимание, что теперь студенты отсортированы по оценке в порядке убывания. Когда оценки равны (как у Алисы и Дэвида, у обоих 85,5), они сортируются по возрасту в порядке возрастания.

Интерфейс Comparable обеспечивает естественный порядок для наших объектов, но что, если мы хотим сортировать объекты разными способами в разное время? Например, иногда мы можем захотеть отсортировать студентов по имени, а в другое время — по возрасту. Именно здесь вступает в игру интерфейс Comparator, который мы рассмотрим на следующем шаге.

Использование Comparator для пользовательского упорядочивания

Интерфейс Comparable обеспечивает естественный порядок для нашего класса, но иногда нам нужно сортировать объекты разными способами в зависимости от разных критериев. Именно здесь пригодится интерфейс Comparator.

Понимание интерфейса Comparator

Интерфейс Comparator<T> определяет метод compare(T o1, T o2), который сравнивает два объекта одного и того же типа. В отличие от Comparable, который реализуется самим классом, который сравнивается, Comparator — это отдельный класс или лямбда-выражение, которое может определять различные критерии упорядочивания.

Создание пользовательских компараторов

Давайте создадим несколько компараторов для нашего класса Student:

  1. NameComparator: Сортирует студентов по имени (в алфавитном порядке)
  2. AgeComparator: Сортирует студентов по возрасту (по возрастанию)
  3. GradeComparator: Сортирует студентов по оценке (по убыванию)

Создайте новый файл с именем StudentComparators.java со следующим содержимым:

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

Тестирование компараторов

Теперь давайте создадим тестовый класс, чтобы продемонстрировать, как использовать эти компараторы. Создайте файл с именем 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);
        }
    }
}

Компиляция и запуск

Давайте скомпилируем и запустим наш код:

javac Student.java StudentComparators.java ComparatorTest.java
java ComparatorTest

Вывод покажет, как разные компараторы влияют на порядок сортировки студентов:

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)

Ключевые различия между Comparable и Comparator

Понимание различий между Comparable и Comparator важно:

  1. Место реализации:

    • Comparable реализуется самим классом.
    • Comparator реализуется в отдельном классе или как лямбда-выражение.
  2. Количество упорядочений:

    • Comparable определяет одно «естественное упорядочение» для класса.
    • Comparator допускает несколько разных упорядочений.
  3. Сигнатуры методов:

    • Comparable имеет int compareTo(T o)
    • Comparator имеет int compare(T o1, T o2)
  4. Использование:

    • Comparable проще, когда есть очевидный естественный порядок.
    • Comparator более гибкий, когда требуется несколько упорядочений.

На последнем шаге мы создадим реальное приложение, которое использует как Comparable, так и Comparator для управления базой данных студентов.

Создание системы управления студентами

Теперь, когда мы понимаем, как сравнивать объекты с помощью equals(), Comparable и Comparator, давайте создадим простую систему управления студентами, которая объединит все это в практическом приложении.

Создание класса StudentManager

Давайте создадим класс, который будет управлять коллекцией студентов и предоставлять различные операции над ними. Создайте файл с именем 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();
    }
}

Создание основного приложения

Теперь давайте создадим наше основное приложение, которое использует класс StudentManager. Создайте файл с именем 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.");
            }
        }
    }
}

Компиляция и запуск приложения

Давайте скомпилируем и запустим нашу систему управления студентами:

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

Теперь вы увидите интерактивное меню, которое позволяет вам:

  1. Добавлять новых студентов
  2. Отображать всех студентов
  3. Отображать лучших студентов по оценке
  4. Отображать студентов, отсортированных по имени
  5. Отображать студентов, отсортированных по возрасту
  6. Отображать студентов с оценкой выше порогового значения
  7. Отображать студентов, сгруппированных по возрасту
  8. Найти студента по имени
  9. Выйти из приложения

Попробуйте разные варианты, чтобы увидеть, как различные механизмы сравнения, которые мы реализовали, работают в реальном приложении. Например:

  • Вариант 3 использует GradeComparator для поиска лучших студентов по оценке
  • Вариант 4 использует NameComparator для сортировки студентов по имени
  • Вариант 5 использует AgeComparator для сортировки студентов по возрасту

Эта система управления студентами демонстрирует, как изученные нами механизмы сравнения могут применяться в реальном сценарии для эффективного управления и организации данных.

Основные выводы

В этой лабораторной работе вы узнали:

  1. Как создать класс Java с несколькими атрибутами
  2. Как переопределить equals() и hashCode() для правильного сравнения объектов
  3. Как реализовать интерфейс Comparable для определения естественного порядка
  4. Как использовать интерфейс Comparator для пользовательского упорядочивания
  5. Как применять эти концепции на практике

Эти навыки являются фундаментальными для любого Java-разработчика и будут полезны в широком спектре приложений, от обработки данных до пользовательских интерфейсов.

Резюме

В этой лабораторной работе вы узнали, как сравнивать объекты Java на основе нескольких атрибутов. Вы изучили три основных механизма сравнения объектов в Java:

  1. Переопределение методов equals() и hashCode(): Вы узнали, как правильно реализовать эти методы, чтобы проверить, равны ли два объекта на основе их содержимого.

  2. Реализация интерфейса Comparable: Вы узнали, как определить естественный порядок для ваших объектов, реализовав метод compareTo(), который позволяет сортировать объекты на основе нескольких атрибутов.

  3. Использование интерфейса Comparator: Вы изучили, как создавать пользовательские компараторы для сортировки объектов различными способами в зависимости от различных критериев.

Вы применили эти концепции для создания практической системы управления студентами, которая демонстрирует, как эти механизмы сравнения работают вместе в реальном приложении.

Эти навыки являются фундаментальными для эффективного программирования на Java, особенно при работе с коллекциями объектов, которые необходимо сортировать, фильтровать или сравнивать. Понимание сравнения объектов позволяет создавать более эффективные и надежные приложения.

Продолжайте практиковать эти методы, так как они будут бесценны в вашем пути как Java-разработчика.