List 정렬과 Comparator/Comparable 인터페이스 활용
Java에서 리스트나 컬렉션 내 요소를 정렬하는 것은 중요한 기술 중 하나입니다. 컬렉션의 요소가 기본적으로 정렬 가능한지 여부에 따라 두 가지 인터페이스, Comparable과 Comparator,를 사용합니다. 이를 활용하면 단순한 정렬부터 복잡한 다중 필드 정렬까지 구현할 수 있습니다. 아래에서 이 두 인터페이스를 비교하고, Java 8 이후의 기능 확장에 대해 설명하겠습니다.
1. Comparable 인터페이스
Comparable 인터페이스는 객체 자체에 정렬 기준을 정의할 때 사용됩니다. 즉, 객체에 내장된 정렬 기능을 설정할 수 있습니다. Comparable 인터페이스는 단일 정렬 기준만을 제공하기 때문에 간단한 정렬 작업에 적합합니다.
구현 방법:
- 클래스에 Comparable<T> 인터페이스를 구현합니다.
- compareTo 메서드를 오버라이드하여 정렬 기준을 정의합니다.
예제 코드
public class Student implements Comparable<Student> {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student other) {
// 점수를 기준으로 오름차순 정렬
return Integer.compare(this.score, other.score);
}
@Override
public String toString() {
return "Student{name='" + name + "', score=" + score + "}";
}
}
이제 List<Student>를 정렬할 때 Collections.sort() 또는 list.sort() 메서드를 호출하면 score를 기준으로 정렬됩니다.
2. Comparator 인터페이스
Comparator는 객체 외부에 정렬 기준을 제공할 때 사용됩니다. 즉, 기존 클래스의 정렬 방식을 변경하지 않고도 다양한 정렬 기준을 적용할 수 있습니다. Comparator는 다중 필드 정렬이나 복잡한 정렬 로직을 구현할 때 유용합니다.
구현 방법:
- Comparator<T> 인터페이스를 구현하거나, 정적 메서드로 구현된 람다식을 사용합니다.
- compare 메서드를 오버라이드하여 정렬 기준을 정의합니다.
예제 코드
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 85),
new Student("Bob", 92),
new Student("Charlie", 78)
);
// 점수 기준으로 내림차순 정렬
students.sort(Comparator.comparingInt(Student::getScore).reversed());
// 이름 기준으로 정렬
students.sort(Comparator.comparing(Student::getName));
// 다중 기준 정렬: 점수 내림차순 후 이름 오름차순
students.sort(
Comparator.comparingInt(Student::getScore).reversed()
.thenComparing(Student::getName)
);
students.forEach(System.out::println);
}
}
3. Java 8 이후의 Comparator 확장 메서드
Java 8부터 Comparator 인터페이스는 다양한 메서드를 통해 더욱 강력해졌습니다. 대표적인 메서드는 comparing, thenComparing, reversed입니다. 이를 사용하면 코드가 더욱 직관적이며 간결해집니다.
확장 메서드 예시:
- Comparator.comparing(Function<T, U> keyExtractor): 특정 키를 기준으로 정렬합니다.
- thenComparing(Function<T, U> keyExtractor): 첫 번째 정렬 기준이 같은 경우 두 번째 기준으로 정렬합니다.
- reversed(): 정렬 결과를 뒤집습니다.
예제 코드
students.sort(Comparator.comparing(Student::getScore)
.thenComparing(Student::getName)
.reversed());
4. 사용자 정의 객체 리스트 정렬 실습
아래는 Comparator와 Comparable을 활용하여 사용자 정의 객체 리스트를 정렬하는 실습 코드입니다.
실습 코드
public class Main {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 90),
new Student("David", 95),
new Student("Bob", 90),
new Student("Charlie", 85)
);
// 점수 오름차순 후 이름 오름차순 정렬
students.sort(Comparator.comparingInt(Student::getScore)
.thenComparing(Student::getName));
students.forEach(System.out::println);
}
}
5. 정리
- Comparable은 클래스 내부에 내장된 단일 정렬 기준을 정의할 때 사용됩니다.
- Comparator는 클래스 외부에서 다양한 정렬 기준을 제공할 때 사용되며, 다중 필드 정렬을 구현하기에 적합합니다.
- Java 8 이후에는 람다식과 메서드 참조, 다양한 Comparator 확장 메서드를 활용하여 더 간결하고 읽기 쉬운 코드를 작성할 수 있습니다.