사용자 정의 정렬을 위한 Comparator 사용
Comparable 인터페이스는 클래스에 대한 자연스러운 순서를 제공하지만, 때로는 다른 기준에 따라 객체를 다른 방식으로 정렬해야 합니다. 이럴 때 Comparator 인터페이스가 유용합니다.
Comparator 인터페이스 이해
Comparator<T> 인터페이스는 동일한 유형의 두 객체를 비교하는 compare(T o1, T o2) 메서드를 정의합니다. 비교되는 클래스 자체에서 구현되는 Comparable과 달리, Comparator는 다양한 정렬 기준을 정의할 수 있는 별도의 클래스 또는 람다 표현식입니다.
사용자 정의 Comparator 생성
Student 클래스에 대한 여러 개의 comparator 를 만들어 보겠습니다.
NameComparator: 학생을 이름순 (알파벳순) 으로 정렬합니다.
AgeComparator: 학생을 나이순 (오름차순) 으로 정렬합니다.
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 의 주요 차이점
Comparable과 Comparator의 차이점을 이해하는 것이 중요합니다.
-
구현 위치:
Comparable은 클래스 자체에서 구현됩니다.
Comparator는 별도의 클래스 또는 람다 표현식으로 구현됩니다.
-
정렬 수:
Comparable은 클래스에 대한 단일 "자연스러운 순서"를 정의합니다.
Comparator는 여러 가지 다른 정렬을 허용합니다.
-
메서드 시그니처:
Comparable에는 int compareTo(T o)가 있습니다.
Comparator에는 int compare(T o1, T o2)가 있습니다.
-
사용법:
Comparable은 명백한 자연스러운 순서가 있는 경우 더 간단합니다.
Comparator는 여러 정렬이 필요한 경우 더 유연합니다.
마지막 단계에서는 Comparable과 Comparator를 모두 사용하여 학생 데이터베이스를 관리하는 실제 애플리케이션을 만들 것입니다.