如何基于多个属性比较 Java 对象

JavaBeginner
立即练习

介绍

基于多个属性比较 Java 对象是任何 Java 开发者必备的一项技能。这项能力使得在应用程序中对数据进行高效的排序、过滤和组织成为可能。在这个实验(Lab)中,你将学习如何在 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)。我们还包括了一个构造函数,每个属性的 getter 方法,以及一个 toString() 方法,以便更容易地显示学生信息。

测试 Student 类

让我们创建一个 main 类来测试我们的 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 对象。这在 Java 中被称为身份比较,它检查两个引用是否指向内存中的同一个对象。

然而,我们经常需要根据它们的内容或属性来比较对象,这被称为相等性比较。Java 提供了几种实现此目的的机制:

  1. 重写 equals() 方法
  2. 实现 Comparable 接口
  3. 使用 Comparator 接口

在接下来的步骤中,我们将实现这些机制,以便根据学生的属性来比较 Student 对象。

实现 equals() 和 hashCode() 方法

在深入研究 ComparableComparator 接口之前,让我们首先在 Student 类中实现 equals()hashCode() 方法。这些方法是 Java 中正确进行对象比较的基础。

理解 equals() 和 hashCode()

在 Java 中,equals() 方法用于根据它们的内容检查两个对象是否相等,而 hashCode() 方法生成一个表示该对象的数值。这两个方法协同工作,尤其是在对象存储在 HashMapHashSet 等集合中时。

一个正确的实现应该遵循以下规则:

  • 如果两个对象根据 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

请注意,尽管 alicealiceDuplicate 是不同的对象(如 == 比较所示),但它们根据我们的 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

请注意,学生现在按他们的成绩以降序排列。当成绩相等时(如 Alice 和 David,都是 85.5),它们按年龄升序排列。

Comparable 接口为我们的对象提供了一个自然排序,但是如果我们想在不同的时间以不同的方式对对象进行排序呢?例如,有时我们可能希望按姓名对学生进行排序,而其他时候按年龄排序。这就是 Comparator 接口的作用,我们将在下一步中探讨它。

使用 Comparator 进行自定义排序

Comparable 接口为我们的类提供了一个自然排序,但有时我们需要根据不同的标准以不同的方式对对象进行排序。这时,Comparator 接口就派上用场了。

理解 Comparator 接口

Comparator<T> 接口定义了一个方法 compare(T o1, T o2),它比较两个相同类型的对象。与由被比较的类实现的 Comparable 不同,Comparator 是一个单独的类或 lambda 表达式,可以定义各种排序标准。

创建自定义 Comparator

让我们为我们的 Student 类创建几个 comparator:

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

测试 Comparators

现在让我们创建一个测试类来演示如何使用这些 comparator。创建一个名为 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

输出将演示不同的 comparator 如何影响学生的排序顺序:

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 之间的主要区别

理解 ComparableComparator 之间的区别很重要:

  1. 实现位置

    • Comparable 由类本身实现。
    • Comparator 在一个单独的类中实现,或者作为 lambda 表达式。
  2. 排序数量

    • Comparable 为一个类定义一个单一的“自然排序”。
    • Comparator 允许有多个不同的排序。
  3. 方法签名

    • Comparableint compareTo(T o)
    • Comparatorint compare(T o1, T o2)
  4. 用法

    • 当存在明显的自然排序时,Comparable 更简单。
    • 当需要多个排序时,Comparator 更灵活。

在最后一步,我们将创建一个真实世界的应用程序,该应用程序使用 ComparableComparator 来管理学生数据库。

构建一个学生管理系统

现在我们已经了解了如何使用 equals()ComparableComparator 来比较对象,让我们构建一个简单的学生管理系统,将所有内容整合到一个实际的应用程序中。

创建 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 接口:你探索了如何创建自定义 comparator,以便根据各种标准以不同的方式对对象进行排序。

你已经应用了这些概念来构建一个实用的学生管理系统,该系统演示了这些比较机制如何在实际应用程序中协同工作。

这些技能对于有效的 Java 编程至关重要,尤其是在处理需要排序、过滤或比较的对象集合时。理解对象比较可以让你构建更高效、更健壮的应用程序。

继续练习这些技术,因为它们将在你作为 Java 开发人员的旅程中发挥重要作用。