[Java] 심화 - Comparator, Comparable

이지연·2025년 12월 20일

개요

아래의 내용은 java_grammer 레파지토리 C05AnonymousLambda 디렉터리에 저장되어있는 내용을 정리함


Comparator vs Comparable 개요

자바에서는 비교를 위한 인터페이스로 대표적으로 두 가지가 있음.

인터페이스메서드특징
Comparatorint compare(T o1, T o2)외부 정렬 기준. 별도 비교 객체를 생성해서 사용함.
Comparableint compareTo(T o)내부 정렬 기준. 비교 대상 클래스 자체에 구현함.

Comparator 기본 사용법

숫자 리스트 정렬

List<Integer> myList = new ArrayList<>();
myList.add(10); myList.add(20); myList.add(30);

// 기본 오름차순 정렬
myList.sort(Comparator.naturalOrder()); 

// 람다식으로 직접 구현한 오름차순 정렬
myList.sort((o1, o2) -> o1 - o2);

정렬 규칙 (중요!)

(o1, o2) -> o1 - o2  → **o1이 먼저** = **오름차순**
(o1, o2) -> o2 - o1  → **o2가 먼저** = **내림차순**

compare 메서드의 반환값 규칙:

  • 음수(negative)o1o2보다 앞에 배치
  • 0 → 순서 유지
  • 양수(positive)o2o1보다 앞에 배치

문자열 리스트 정렬

List<String> myList2 = new ArrayList<>();
myList2.add("java"); myList2.add("python"); myList2.add("c++");

// 기본 내림차순 정렬 (알파벳순)
Collections.sort(myList2, Comparator.reverseOrder());
System.out.println("기본 내림차순 정렬 : " + myList2); 
// [python, java, c++]

// 알파벳순 내림차순
Collections.sort(myList2, (o1, o2) -> o2.compareTo(o1));
System.out.println("알파벳순 내림차순 정렬 : " + myList2); 

// 문자열 길이 기준 오름차순
Collections.sort(myList2, (o1, o2) -> o1.length() - o2.length());
System.out.println("문자길이순 오름차순 정렬 : " + myList2); 
// [c++, java, python]

복합 정렬 기준 (복잡한 조건)

문자열 길이로 정렬하되, 길이가 같으면 알파벳순으로 정렬하는 예시임.

myList2.add("HTML"); // 길이 4

Collections.sort(myList2, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        if (o1.length() == o2.length()) {
            return o1.compareTo(o2);  // 길이 같으면 알파벳순
        } else {
            return o1.length() - o2.length();  // 길이 다르면 길이순
        }
    }
});
System.out.println("문자길이순 & 알파벳순 정렬 오름차순 : " + myList2);
// [c++, java, HTML, python]

람다식으로 간결하게 표현하면:

Collections.sort(myList2, (o1, o2) -> {
    if (o1.length() == o2.length()) return o1.compareTo(o2);
    return o1.length() - o2.length();
});

실습예제: 리스트 안의 배열 정렬

요구사항: 리스트 안의 배열에 1번째 index를 기준으로 오름차순 정렬
[{4,5},{1,2},{5,0},{3,1}] → [{5,0},{3,1},{1,2},{4,5}]

List<int[]> arrList = new ArrayList<>();
arrList.add(new int[]{4,5});
arrList.add(new int[]{1,2});
arrList.add(new int[]{5,0});
arrList.add(new int[]{3,1});

Collections.sort(arrList, new Comparator<int[]>() {
    @Override
    public int compare(int[] o1, int[] o2) {
        return o1[1] - o2[1];  // 1번째 인덱스(5,2,0,1) 기준 정렬
    }
});

for (int[] arr : arrList) {
    System.out.println(Arrays.toString(arr));
}
// [5, 0], [3, 1], [1, 2], [4, 5]

다른 자료구조에서의 Comparator 사용

PriorityQueue, TreeSet, TreeMap 등에서도 동일하게 Comparator 사용함.

// PriorityQueue: 문자열 길이순 정렬
Queue<String> pq = new PriorityQueue<>((o1, o2) -> o1.length()-o2.length());

// TreeSet: 문자열 길이순 정렬 (중복 제거)
Set<String> treeSet = new TreeSet<>((o1, o2) -> o1.length() - o2.length());

백준 알고리즘 활용 예시

백준 - 최대값 힙 문제

PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);

TODO 백준 문제:

  • 단어정렬
  • 절대값 힙
  • 선긋기

실습예제: Student 객체 정렬

Student 클래스 요구사항: name, age 변수 / 생성자 / getter / toString

class Student implements Comparable<Student> {
    private String name;
    private int age;
    
    public Student(String name, int age) { this.name = name; this.age = age; }
    public String getName() { return name; }
    public int getAge() { return age; }
    
    @Override
    public String toString() {
        return "{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}';
    }
    
    // Comparable: this와 o 비교
    @Override
    public int compareTo(Student o) {
        return this.getName().compareTo(o.getName());  // 이름순 오름차순
    }
}

Student 객체 생성 및 정렬

List<Student> students = new ArrayList<>();
students.add(new Student("lee", 20));
students.add(new Student("kim", 45));
students.add(new Student("park", 13));
students.add(new Student("hong", 20));

// 방법(1) Comparator 익명객체: 나이순
/*
Collections.sort(students, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();  // 13,20,20,45
    }
});
*/

// 방법(2) Comparable 구현: 이름순 (클래스 자체에 compareTo 구현)
Collections.sort(students);  // 자동으로 Student.compareTo 호출
System.out.println(students);
// [{name='hong', age='20'}, {name='kim', age='45'}, {name='lee', age='20'}, {name='park', age='13'}]

Comparable 단점: 객체 자체에 정렬 기준 고정. 유연성 떨어짐.


전체 정렬 메서드 정리

메서드설명Comparator 필요 여부
list.sort(comparator)List 정렬필요
Collections.sort(list)List 정렬 (기본/Comparable)불필요
Collections.sort(list, comparator)List 정렬 (커스텀)필요
new PriorityQueue<>(comparator)우선순위큐필요
new TreeSet<>(comparator)정렬 집합필요

요약 정리

  1. Comparator외부에서 정렬 기준을 정의하는 인터페이스임.
  2. 람다식으로 간결하게 구현 가능함: (o1, o2) -> o1 - o2
  3. 복합 정렬if-else나 다중 조건으로 구현함.
  4. 자바 대부분의 정렬 함수가 Comparator 객체를 매개변수로 요구함.
  5. 백준 알고리즘에서 최대/최소 힙, 단어 정렬 등에 필수임.
  6. Comparable은 클래스 자체에 compareTo 구현. 단점: 유연성 부족.

Stream 연계: list.stream().sorted((o1,o2)->o1.age-o2.age)

profile
Eazy하게

0개의 댓글