[Java] Comparator, Comparable을 이용한 다중 조건 정렬 구현

YJ KIM·2022년 9월 14일
2
post-custom-banner

목차

  1. Comparator, Comparable이란?
  2. Comparator
  3. Comparable
  4. Comparator vs Comparable
  5. Comparator + Comparable을 이용한 다중 조건 정렬

프로젝트 구현 중에, 정확도(1순위 정렬) -> 거리순(2순위 정렬)로 정렬하는 것을 구현해야 했다. Comparator, Comparable을 알고는 있었지만 그때그때 구글링해서 사용하는 것이 전부였기 때문에 이번 기회에 정리하여 확실히 알고가려고 한다!

0. Comparator, Comparable이란?

Comparable, Comparator 모두 함수형 인터페이스(하나의 추상 메소드를 가지는 인터페이스)로 비교에 대한 기준을 정의하는 인터페이스다.

📍 comparable이 @FunctionalInterface annotation이 없어, 함수형 인터페이스가 아니라는 의견도 있지만 나는 하나의 추상 메소드를 가지기 때문에 함수형 인터페이스라고 생각한다.

관련 의견은 : stackoverflow_is-comparable-interface-a-functionalinterface
를 확인하면 좋을 것 같다.

비교에 대한 기준을 정의하는 이유는, 이를 정렬 시에 사용하기 위함이다.

1. Comparator

Comparator interface의 정의를 보면,

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

로 compare이라는 추상 메소드를 정의해야 한다.

사실 comparator interface의 정의를 살펴보면 메소드가 정말 많고 추상 메소드가 equals, compare의 두 개가 존재한다.

하지만 equals 메소드는, java.lang.Object의 equals method를 overiding하는 추상 메소드로 이는 추상 메소드로 간주하지 않는다고 한다.
(관련 내용 참고: mkyong.com/java8/is-comparator-a-function-interface-but-it-has-two-abstract-methods )

1-1. Comparator 내림차순 구현

        //내림차순
        Collections.sort(testList, new Comparator<Test>() {
            @Override
            public int compare(Test o1, Test o2) {
                if(o1.getX()< o2.getX()) return 1;
                else if(o1.getX()==o2.getX()) return 0;
                else return -1;
            }
        });

1-2. Comparator 오름차순 구현

        //오름차순
        Collections.sort(testList, new Comparator<Test>() {
            @Override
            public int compare(Test o1, Test o2) {
                if(o1.getX()< o2.getX()) return -1;
                else if(o1.getX()==o2.getX()) return 0;
                else return 1;
            }
        });

2. Comparable

Comparable interface의 정의를 보면,

public interface Comparable<T> {
    public int compareTo(T o);
}

의 구조로 구성된다. Comparator와 동일하게 compareTo를 재정의하여, 비교 기준을 정의하면 Comparable을 이용한 정렬을 수행할 수 있다.

2-1. Comparable 내림차순 구현

    //내림차순
    @Override
    public int compareTo(Test o) {
        return o.getX()-this.getX();
    }

2-2. Comparable 오름차순 구현

    //오름차순
    @Override
    public int compareTo(Test o) {
        return this.getX()-o.getX();
    }

3. Comparator vs Comparable

😯: Comparator과 Comparable은 같은 역할을 하는데 왜 다른 거야?
라는 물음을 가질 수 있다.

정답이다!
같은 역할을 하지만, 비교 대상이 다르다.

Comparator - Compare(T o1, T o2) 정의
Comparable - CompareTo (T o) 정의

Comparator는 두 객체를 비교하는 방향으로 비교 기준을 세우고,
Comparable은 현재 객체와 다른 객체를 비교하는 방향으로 비교 기준을 세운다.

(+ 추가적으로는, package 차이로 import를 다르게 해줘야 한다는 차이점도 존재한다.)

4. Comparator과 Comparable을 이용한 다중 조건 정렬 구현

다중 조건을 정렬하기 위해서는, Comparator와 Comparable을 같이 구현하여 사용하면 편리하다.

나의 경우에는 정렬 1순위가 정확도, 2순위가 거리순이었다.
-> 정확도가 동일한 경우에는 거리 순으로 우선순위를 매겨 정렬하는 식으로 구성하였다.
(📌 정확도는 높은 순으로, 거리는 짧은 순으로)

        //정확도 순으로 정렬(거리순은 2차 정렬)
        Collections.sort(objList,new Comparator<Obj>() {
            @Override
            public int compare(Obj s1, Obj s2) {
                if(s1.getScore()<s2.getScore()) return 1;
                else if(s1.getScore()==s2.getScore())
                    return s1.compareTo(s2);
                else
                    return -1;
            }
        });

제네릭이 Obj type인 Comparator를 생성하여 sort의 기준으로 넣어준다.
이 때, compare abstract method를 재정의한다.

score(정확도)에 따라 내림차순으로 정렬하고,
만약 score가 같은 경우, 2차 정렬 기준인 거리 순(오름차순)으로 정렬한다.

-> 거리 순은 Obj에 Comparable 인터페이스를 상속하여, compareTo abstract method를 재정의하여 구현하였다.

    @Override
    public int compareTo(Obj o) {
        return (int)(this.getDist()-o.getDist());
    }

Obj 클래스에서 compareTo를 위와 같이 재정의하였다.

👀 구현 과정에서의 어려움

정확도 순으로 정렬할 때, 정확도 순으로 정렬이 되지 않았다.
score가 e-9와 같이 매우 작은 크기였기 때문에 정렬이 되지 않았다고 추측 중이다.

원래는 <, > 등의 부등호를 사용하지 않고 빼는 s1과 s2의 score를 뺀 값을 반환하였는데
double형인 score의 크기가 매우 작아 부등호로 비교하여 반환 값을 1, -1, 0으로 return 하도록 하였다.

→ 수정하니 비로소 구현이 성공적으로 되었다.


[참고한 포스팅]

https://st-lab.tistory.com/243

profile
모르면 쓰지 말고 쓸 거면 알고 쓰자
post-custom-banner

0개의 댓글