[TIL] Comparable, Comparator

기면지·2021년 8월 11일
1

TIL

목록 보기
5/10
post-thumbnail

안녕하세요!
오늘 공부한 Java의 Comparable , Comparator 에 대해서 작성해보겠습니다.


Comparable

Comparable 은 인터페이스입니다.
ComparablecompareTo() 메소드는 객체를 정렬하는데 사용됩니다.
자바에서 같은 인스턴스를 비교하는 클래스들은 모두 Comparable 인터페이스를 구현하고 있습니다.

int[] arr = {50, 30, 10, 23, 45};
System.out.println("정렬전 : " + Arrays.toString(arr));
Arrays.sort(arr);
System.out.println("정렬후 : " + Arrays.toString(arr));

String[] women = {"김연경", "박정아", "김수지", "김희진", "이소영"};
System.out.println("정렬전 : " + Arrays.toString(women));
Arrays.sort(women);
System.out.println("정렬후 : " + Arrays.toString(women));

메인 메소드에서 위 코드를 실행시키면 어떤 결과가 도출될까요?

int 타입 배열은 물론, String 타입의 배열도 정렬됩니다.


위 사진은 String 클래스의 선언부입니다.
보시면, Comparable<String> 을 구현하고 있는 것을 확인할 수 있습니다.


compareTo() 메소드도 재정의하고 있는 것을 확인할 수 있습니다.

그렇다면, compareTo() 메소드는 무엇을 비교하는 메소드일까요?
현재 자신과 같은 타입의 객체와 비교합니다.
즉, 나와 다른 사람을 비교하는 것이겠죠. (String 의 메소드 파라미터 이름도 anotherString 이네요)
같은 타입의 다른 객체와 자신을 비교해 값을 int 타입으로 리턴합니다.
음수면 자신이 다른 객체보다 작다, 0이면 동일하다, 양수면 자신이 다른 객체보다 큰 것을 의미합니다.

compareTo() 메소드는 제네릭 타입의 객체를 비교할 때 사용합니다.
primitive 타입이라면 Integer, Character, Double ... 의 박싱 타입으로 오토 박싱 되어서 compareTo() 메소드를 사용할 수 있습니다.

class Student {
	int no, score;
}

간단한 Student 클래스입니다.
다른 Student 객체와 score 을 기준으로 변경하고 싶다면 어떻게 해야할까요?

class Student implements Comparable<Student> {
	int no, score;
    
    @Override
    public int compareTo(Student o) {
    	return this.score - o.score;
    }
}

Comparable 인터페이스를 구현해 compareTo() 메소드를 재정의한다면, 현재 객체만의 비교 기준을 가질 수 있습니다.

하지만 객체의 비교 기준을 바꾸고 싶을 때, 클래스에 들어가서 재정의된 compareTo() 메소드를 변경해야 할까요?
StringcompareTo() 를 오름차순이 아닌 내림차순으로 변경하고 싶다면 자바 API를 변경해야 할까요?

Comparator

이 때 필요한 개념이 Comparator 인터페이스입니다.
Comparable 는 자기 자신과 다른 것 과의 비교이므로 클래스 기준에서 compareTo() 를 재정의하고 사용해야 했습니다.

하지만 Comparator 는 자기 자신과는 아무런 상관이 없고, 그저 다른 두 객체의 비교를 진행합니다.
구현해야 할 메소드인 compare() 도 매개변수가 2개입니다.
즉, 다른 두 객체를 받아서 비교만 Comparator 가 진행합니다.
비교 기준은 Comparable 과 동일하게 음수면 자신이 다른 객체보다 작다, 0이면 동일하다, 양수면 자신이 다른 객체보다 큰 것을 의미합니다.

클래스에 Comparator 를 구현해도 되지만, 비교 기준을 변경하고 싶다면 익명 함수나 람다로 많이 사용됩니다.
자주 사용되는 Arrays.sort() 메소드의 매개변수에도 (정렬할 객체, Comparator<T>) 가 있습니다.
정렬 기준을 Comparator 로 선언하는 것이죠

public static void main(String[] args) {
    Student[] students = {
            new Student(1, 10),
            new Student(3, 50),
            new Student(2, 80),
            new Student(4, 10)
    };
}

메인 메소드에 정렬할 Student 배열을 선언합니다.

static class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score - o2.score;
    }
}

위의 방법은 Comparator 인터페이스를 구현하는 클래스를 하나 생성해 정렬하는 방법입니다.
위처럼 클래스를 선언하고, 메인 메소드 안에서 Arrays.sort(students, new StudentComparator()); 를 실행하면 StudentComparator 클래스에 선언된 compare 을 기준으로 정렬됩니다.

하지만, 나와는 상관없는 다른 두 객체를 비교할 예정인데 굳이 클래스를 생성할 이유는 없습니다.

Arrays.sort(students, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o2.score - o1.score;
    }
});

그럴 땐 한 번 사용하고 끝나는 익명 클래스를 만들어서 사용할 수도 있습니다.
위의 예제는 Student 점수 기준 내림차순 입니다.

Arrays.sort(students, (o1, o2) -> o2.score - o1.score)

더 간단하게는 람다도 사용할 수 있습니다.
가독성 있고 코드가 단순해 Comparator 를 사용할 때는 람다를 자주 사용하는 것 같습니다.

그렇다면 점수가 같을 때, 학생 번호로 정렬하는 방법은 없을까요?

Arrays.sort(students, (o1, o2) -> (o2.score != o1.score)? o2.score - o1.score: o2.no - o1.no);

3항 연산자를 이용하면 쉽게 끝납니다.

요약

자기 자신과, 다른 객체의 비교를 정해진 기준으로 해야한다Comparable<T>compareTo(T o) 를 사용하고,
자기 자신과는 상관 없이 다른 두 객체의 비교를 해야하는데, 굳이 기준이 항상 같지 않다Comparator<T>compare(o1, o2) 를 사용하면 될 것입니다.


알고리즘 리뷰

백준 배열 돌리기 4
백준 요세푸스 문제


마무리

어제의 TIL이 밀렸지만.. 오늘 TIL 먼저 작성해봤습니다!
항상 헷갈렸던 ComparableComparator 를 확실하게 이해하고 사용할 수 있을 것 같아서 너무 좋네요!!

이번 포스팅도 읽어주셔서 감사합니다 :)

profile
𝙎𝙈𝘼𝙇𝙇 𝙎𝙏𝙀𝙋𝙎 𝙀𝙑𝙀𝙍𝙔 𝘿𝘼𝙔

0개의 댓글