[JAVA] 객체의 비교 - Comparable과 Comparator

Comparable과 Comparator는 둘 다 객체 비교를 위한 인터페이스이다. 따라서 이것들의 사용을 위해서는 인터페이스 내에 선언된 메소드를 반드시 오버라이드 해주어야 한다.

 

요즘 정리하고 있는 jpa에서의 값 타입에서 컬럼 분리를 위해서 equalsTo 메소드를 오버라이드 해주는 것과 비슷한 맥락인데, 이 비교를 위한 메소드도 자바에서 primitive type 자료형은 값의 비교가 이루어지고, reference type은 참조 비교가 이루어진다는 사실 때문이다.

 

풀어서 설명하자면, int나 long같은 primitive type의 경우 부등호를 이용하여 간단하게 비교할 수 있다.

int a=1;
int b=2;
System.out.println(a<b);
//true

그런데 만약에 비교대상이 객체라면? 객체의 비교의 기준을 정해주어야 한다. 바로 그 기준을 정하기 위해 Comparable 또는 Comparator를 오버라이드하면 된다.

Car a = new Car();
Car b = new Car();
System.out.println(a<b);
//기준이 크기?높이?무게? 알 수 없다.

 

그렇다면 Comparable과 Comparator의 차이는 무엇일까? Comparable은 자기 자신과 매개변수를 비교할 때의 규칙을 정의하고, Comparator는 두 매개변수를 비교하기 위해서 사용된다. 

 

 

 

1. Comparable<T>

 

 먼저 Comparable의 자바 공식 API사이트에 들어가보면, compareTo 메소드를 오버라이드해주어야 한다고 나와있다. 위에서 객체의 비교를 위해 비교 메소드를 오버라이드해주어야 한다고 했다. Comparable의 그 비교 메소드가 compareTo인 것이다.

 

public class Car implements Comparable<Car> {
    String name;
    int price;
    
    @Override
    public int compareTo(Car o) {
        //비교
    }
}

이렇게 간단하게 클래스가 Comparable를 구현하도록 설정하고, compareTo를 오버라이드해주면 된다.

그렇다면 compareTo 메소드는 어떻게 작성해주어야 할까? 먼저 compareTo가 int형이므로 int형 값을 리턴한다는 것은 알 수 있다. 이 때 리턴값의 규칙은 이렇다.

자신보다
1. 크면 > 음수값 반환
2. 같으면 > 0반환
3. 작으면 > 양수값 반환

간단하게 자신의 값과 비교값의 차이의 부호에 따라 반환한다고 이해하면 된다. 그렇기 때문에, 만약 Car클래스를 가격을 기준으로 비교한다고 하면,

public class Car implements Comparable<Car> {
    String name;
    int price;
    
    @Override
    public int compareTo(Car o) {
        return this.price - o.price;
    }
}

과 같이 간단하게 작성할 수 있다.

 

 

2. Comparator<T>

 

 

Comparator도 Comparable과 비교 대상이 바뀌었다는 점을 빼면 동일하게 동작한다.

public class Car implements Comparator<Car> {
    String name;
    int price;
    
    @Override
    public int compare(Car o1, Car o2) {
        return o1.price - o2.price;
    }
}

차이점은 자기 자신과 매개변수의 차이를 리턴하는 Comparable의 compareTo 메소드와 다르게,

첫번째 인자와 두번째 인자의 차이를 리턴한다는 점이다.

 

 

 

 

 

그렇다면 여기서 리턴된 int형 값은 정렬 컬렉션들에서는 어떻게 이용할까? Java에서는 선행 원소와 후행 원소의 차가 음수가 나오면 교환이 이루어지지 않고, 양수가 나오면 교환하는 식으로 compare, compareTo메소드를 이용하여 오름차순(순서대로 커지도록)으로 정렬한다.

 

대표적으로 Arrays.sort()나 Collections.sort()같은 메소드에 익명 객체로 넘겨서 정렬의 기준을 설정해줄 수 있다.

//백준 1931번 코드 일부
int[][] arr = new int[N+1][3];

Arrays.sort(arr, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {

                if(o1[2]==o2[2])
                    return o1[1]-o2[1];

                return o1[2]-o2[2];
                //
            }
        });

Arrays.sort();의 인자로 int[][]형 배열 arr과 compare메소드가 오버라이드된 Comparator 객체가 들어간 것을 볼 수 있다.

먼저 2차원 배열인 arr을 1차원 배열의 배열로 표현하였다. arr[1][1]이면 arr[1]의 [1]번째 index 이런식으로.

 

결국 o1[2]와 o2[2]을 비교하여 오름차순으로 정렬하고, 같다면 o1[1]과 o2[1]을 비교하여 오름차순으로 정렬하는 방식으로 동작하게 된다. 내림차순으로 정렬하고 싶다면 return값의 부호가 반대가 되도록 반환해주면 될 것이다.

 

 

추가로, primitive의 경우 정렬이 불가능하고, 대신 Wrapper클래스를 이용해서 정렬해주어야 한다.

또한, Comparator에서의 람다 사용을 이해하고 싶다면 함수형 인터페이스에 대해서 알아보면 된다.

(https://eckrin.tistory.com/141)

'[ Languages ] > Java' 카테고리의 다른 글

[Java] 자바의 동작 원리와 특징  (0) 2022.07.08
[Java] 자바의 싱글톤 패턴  (0) 2022.07.02
[JAVA] BufferedReader, BufferedWriter  (0) 2022.02.13
[Java] equals와 hashCode  (0) 2022.02.01
[Java] static에 관한 생각  (0) 2022.01.24