Java 객체 비교: 여러 속성을 기반으로 하는 방법

JavaBeginner
지금 연습하기

소개

여러 속성을 기반으로 Java 객체를 비교하는 것은 모든 Java 개발자에게 필수적인 기술입니다. 이 기능을 통해 애플리케이션에서 데이터를 효율적으로 정렬, 필터링 및 구성할 수 있습니다. 이 랩에서는 여러 속성을 가진 객체를 처리하기 위해 Java 에서 다양한 비교 메커니즘을 구현하는 방법을 배우게 됩니다.

equals() 메서드 재정의, Comparable 인터페이스 구현, Comparator 인터페이스 사용 등 Java 에서 객체를 비교하는 기본적인 접근 방식을 살펴볼 것입니다. 실용적인 예제와 실습을 통해 각 접근 방식을 언제, 어떻게 사용해야 하는지에 대한 확실한 이해를 얻게 될 것입니다.

여러 속성을 가진 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 클래스 테스트

Student 클래스를 테스트하기 위한 main 클래스를 만들어 보겠습니다. 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 에서 identity comparison이라고 하며, 두 개의 참조가 메모리에서 동일한 객체를 가리키는지 확인합니다.

그러나 종종 equality comparison이라고 하는 내용 또는 속성을 기반으로 객체를 비교해야 합니다. Java 는 이를 위해 몇 가지 메커니즘을 제공합니다.

  1. equals() 메서드 재정의
  2. Comparable 인터페이스 구현
  3. Comparator 인터페이스 사용

다음 단계에서는 이러한 메커니즘을 구현하여 속성을 기반으로 Student 객체를 비교할 것입니다.

equals() 및 hashCode() 메서드 구현

ComparableComparator 인터페이스를 살펴보기 전에 먼저 Student 클래스에서 equals()hashCode() 메서드를 구현해 보겠습니다. 이러한 메서드는 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. 각 속성을 동일성 (equality) 에 대해 비교합니다.

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)가 있습니다. 이 메서드는 다음을 반환합니다.

  • 현재 객체가 다른 객체보다 작으면 음수 정수
  • 현재 객체가 다른 객체와 같으면 0
  • 현재 객체가 다른 객체보다 크면 양수 정수

이 인터페이스를 구현함으로써 클래스의 객체가 정렬되는 방식을 정의할 수 있습니다.

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 구현 테스트 생성

Comparable 구현을 테스트하기 위해 새 파일 ComparableTest.java를 만들어 보겠습니다.

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는 다양한 정렬 기준을 정의할 수 있는 별도의 클래스 또는 람다 표현식입니다.

사용자 정의 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());
        }
    }
}

Comparator 테스트

이제 이러한 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는 별도의 클래스 또는 람다 표현식으로 구현됩니다.
  2. 정렬 수:

    • Comparable은 클래스에 대한 단일 "자연스러운 순서"를 정의합니다.
    • Comparator는 여러 가지 다른 정렬을 허용합니다.
  3. 메서드 시그니처:

    • Comparable에는 int compareTo(T o)가 있습니다.
    • Comparator에는 int compare(T o1, T o2)가 있습니다.
  4. 사용법:

    • Comparable은 명백한 자연스러운 순서가 있는 경우 더 간단합니다.
    • Comparator는 여러 정렬이 필요한 경우 더 유연합니다.

마지막 단계에서는 ComparableComparator를 모두 사용하여 학생 데이터베이스를 관리하는 실제 애플리케이션을 만들 것입니다.

학생 관리 시스템 구축

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 인터페이스 사용: 다양한 기준에 따라 객체를 다른 방식으로 정렬하기 위해 사용자 정의 comparator 를 만드는 방법을 살펴보았습니다.

이러한 개념을 실제 학생 관리 시스템을 구축하는 데 적용하여 이러한 비교 메커니즘이 실제 응용 프로그램에서 어떻게 함께 작동하는지 보여주었습니다.

이러한 기술은 효과적인 Java 프로그래밍, 특히 정렬, 필터링 또는 비교해야 하는 객체 컬렉션으로 작업할 때 기본입니다. 객체 비교를 이해하면 보다 효율적이고 강력한 응용 프로그램을 구축할 수 있습니다.

Java 개발자로서의 여정에서 매우 중요할 것이므로 이러한 기술을 계속 연습하십시오.