끄적이기

[TIL - 5] Comparable vs Comparator

배씌 2025. 3. 4. 09:49

우선 Comparable과 Comparator는 모두 인터페이스(interface)이다. 즉, 둘 다 사용하려면 인터페이스 내에 선언된 메소드를 '반드시 구현' 해야한다.

 

  • Comparable -> compareTo 메소드 구현
  • Comparator -> compare 메소드 구현

두 인터페이스는 차이는 있지만, 하는 역할은 동일하다. "객체를 비교할 수 있게 한다."

 

하는 역할은 동일하지만, 두 인터페이스의 차이는 비교대상이다.

Comparable은 "자기 자신과 매개변수 객체를 비교" 하는 것이고, Comparator는 "두 매개변수 객체를 비교" 한다는 것이다.


Comparable

public interface Comparable<T> {

	...
    
    public int compareTo(T o);
}

 

Comparable 인터페이스를 살펴보면 위와 같이 선언되어있는 것을 알 수 있다. 선언된 compareTo() 메소드를 보면, 매개변수로 객체 하나를 받는 것을 볼 수 있다. 즉, 이 매개변수와 자기 자신을 비교하는 것이다.

 

예를 들어, Student라는 클래스를 비교해보겠다. 우선 Comparable 인터페이스를 구현하고, compareTo 메소드를 오버라이딩 해주면 된다.

public class Student implements Comparable<Student> {
    int age;
    int classNumber;

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    @Override
    public int compareTo(Student o) {
        // 자기 자신의 age가 o의 age 보다 크다면 양수
        if(this.age > o.age)
            return 1;
        // 자기 자신의 age와 o의 age가 같다면 0
        else if(this.age == o.age)
            return 0;
        // 자기 자신의 age가 o의 age보다 작다면 음수
        else
            return -1;
    }
}

 

compareTo의 리턴값이 int인 것을 볼 수 있다. 이는 자기 자신을 기준으로 객체와의 상태를 비교한 것이다.

(만약 내가 5를 갖고 있을 때, 비교 대상이 3을 갖고 있다면, 나는 값이 2만큼 크다. 반대로 상대방이 7을 갖고있다면 나는 상대방보다 -2 만큼 크다고 할 수 있다. 이를 양수, 0, 음수로 나타내는 것이다.)

 

따라서, 아래처럼 요약할 수 있다.

public class Student implements Comparable<Student> {
    int age;
    int classNumber;

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

Comparator

@FunctionalInterface
public interface Comparator<T> {
    
    ...
    
    int compare(T o1, T o2);
    
    ...
}

 

Comparator 도 마찬가지로, 선언된 compare 메소드를 구현하면 된다. 대신, 두 객체를 비교하는 것이기 때문에, 매개변수가 두개인 것을 볼 수 있다.

import java.util.Comparator;

public class Student implements Comparator<Student> {
    int age;
    int classNumber;

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    @Override
    public int compare(Student o1, Student o2) {
        // o1의 학급이 o2의 학급보다 크다면 양수
        if(o1.classNumber > o2.classNumber){
            return 1;
        }
        // o1의 학급이 o2의 학급과 같다면 0
        else if(o1.classNumber == o2.classNumber){
            return 0;
        }
        // o1의 학급이 o2의 학급보다 작다면 음수
        else
            return -1;
    }
}
import java.util.Comparator;

public class Student implements Comparator<Student> {
    int age;
    int classNumber;

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    @Override
    public int compare(Student o1, Student o2) {
        return o1.classNumber - o2.classNumber;
    }
}

 

Comparable 과 마찬가지로, 선언된 메소드를 구현하고, 간략하게 구현할 수 있다.


Comparator 활용

코드를 작성하다보면, 객체를 정렬해야 하는 상황이 많이 나온다. 예를 들면, Collections.sort() 나, List.sort()를 사용할 때 Comparator를 활용하여 정렬이 가능하다.

 

1. 익명 클래스 사용

Comparator를 implements 하지 않고, 메소드나 서브루틴 내에서 익명 클래스를 활용하여 구현 가능하다.

public class Student {
    int age;
    int classNumber;
    List<Student> students = new ArrayList<>();

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    static void sortByAge(List<Student> students) {
        students.sort(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;
            }
        });
    }
}

 

2. 람다 표현식 활용

위 표현을 람다 표현식을 사용해 더 간결하게 표현 가능

public class Student {
    int age;
    int classNumber;
    List<Student> students = new ArrayList<>();

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    static void sortByAge(List<Student> students) {
        students.sort((o1, o2) -> o1.age - o2.age);
    }
}

 

3. Comparator.comparing() 활용

comparing()을 활용하면 조금 더 가독성을 높일 수 있다.

public class Student {
    int age;
    int classNumber;
    List<Student> students = new ArrayList<>();

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }

    public int getAge() {
        return age;
    }

    static void sortByAge(List<Student> students) {
        students.sort(Comparator.comparing(student -> student.age));
        // Getter 활용
        students.sort(Comparator.comparing(Student::getAge));

    }
}