개발자취

JAVA | Comparator 와 Comparable 본문

개발/Dev | 코딩테스트

JAVA | Comparator 와 Comparable

hnhnhun 2023. 7. 15. 03:12

1. 들어가기

Java.util.Arrays 클래스에는 sort()가 있다. Arrays.sort()를 호출하면 배열을 정렬할 수 있다.

[사진 1] Arrays.sort()

위 [사진 1]을 통해 sort()는 DualPivotQuicksort로 정렬이 이뤄진다는 것은 쉽게 찾아볼 수 있고, JAVA에서 제공하는 API를 사용하는 정도로만 알고 있었기 때문에 정렬에 대한 더 자세한 구현에 대해서는 크게 관심을 갖지 않았었다. 그런데 새로운 사실을 알게 되었다.  Arrays.sort()와 관련하여  JAVA API 문서에 다음과 같이 적힌 내용 때문이다.

All elements in the array must implement the Comparable interface.
[사진 2] sort()
[사진 3] that implement comparable

[사진 2]와 [사진 3]을 통해 Comparable interface가 이렇게나 중요했단 말인가 싶었다. 이 뿐만이 아니다. 코딩테스트 문제에서 요구한 내용에 맞게 구현하기 위해서는, 정렬 조건을 지정해야만 했다. 단일 값이 아닌, class 객체로 생성된 여러 변수가 존재하는 가운데 말이다. 그러기 위해서는 다음과 같이 코드를 작성해야 한다.

[사진 4] Arrays.sort()

[사진 4]의 Comparator를 상속 받아서 overriding 하는 것이다.  Arrays.sort(array, new Comparator<Node>(){...}로 작성하는 것은 익숙하지 않다. 하지만, 이제는 더이상 미룰 수 없다. 정렬 기준을 쉽게 구현하도록 연습해야만 한다. 그래서 정렬 기준 구현을 위한 일련의 과정들을 한 번 훑어보면서 익숙하게 만들어 보려 한다. 따라서 본 글에서는 Comparator나 Comparable을 상속받아 정렬 기준을 구현해 봄으로써 정렬 기준을 구현하는 것을 연습해볼 것이다.
 

2. Comparable, Comparator

Comparable과 Comparator는 모두 인터페이스로 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있다. Comparable을 구현하고 있는 클래스들은 같은 타입의 인스턴스끼리 서로 비교할 수 있는 클래스들인 Interface와 같은 Wrapper 클래스들과 String, Date, File과 같은 것들이고, 기본적으로 오름차순으로 정렬되도록 구현되어 있다. 그래서 Comparable을 구현한 클래스는 정렬이 가능하다는 것을 의미한다. 먼저 Comparable은 java.lang 패키지에 들어있는 인터페이스로 다음과 같은 형태다.

public interface Comparable {
	public int compareTo(Object o);
}

 
다음으로 Comparator는 java.util 패키지에 들어있는 인터페이스로, 다음과 같은 형태다.

public interface Comparator {
	int compare(Object o1, Object o2);
}

compare(), compareTo() 모두 두 객체를 비교하는 목적으로 존재하는 메서드들이다.
두 메서드 모두 int 형을 반환하는데, 두 객체가 같으면 0, 비교하는 값보다 작으면 음수, 크면 양수를 반환하도록 구현해야 한다. 그리고 Comparable을 구현한 클래스들이 기본적으로 오름차순으로 정렬되어 있지만, 내림차순으로 정렬하거나 아니면 정렬 기준을 사용자의 기준으로 정렬하고 싶을 때는 Comparator를 구현해서 정렬 기준을 만들 수 있다. 따라서 정리해보면 다음과 같다.

Comparable : 기본 정렬 기준으로 구현하는데 사용
Comparator : 정렬 기준을 사용자 마음대로 만들고 싶을 때 사용

 

3. 구현

간단한 예제를 통해 실제로 어떻게 사용되는지를 확인해볼 것이다.

import java.util.*;

class ComparatorPractice{
    public static void main(String[] args) {
        String[] strArr = {"AB", "BB", "AA", "DE"};
        Arrays.sort(strArr);
        System.out.println(Arrays.toString(strArr)); // [AA, AB, BB, DE] 문자열의 오름차순(사전순)

        strArr = new String[]{"AB", "BB", "AA", "DE"};
        Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); // 대소문자를 구분하지 않는 형태
        System.out.println(Arrays.toString(strArr)); // [AA, AB, BB, DE]

        strArr = new String[]{"AB", "BB", "AA", "DE"};
        Arrays.sort(strArr, new Descending());
        System.out.println(Arrays.toString(strArr)); // [DE, BB, AB, AA]
    }

    private static class Descending implements Comparator{
        @Override
        public int compare(Object o1, Object o2) {
            if(o1 instanceof Comparable && o2 instanceof Comparable) {
                Comparable c1 = (Comparable)o1; // Object여서 바로 호출 할수 없으므로 형변환
                Comparable c2 = (Comparable)o2;
                return c2.compareTo(c1); // c2.compareTo(c1) or c1.compareTo(c2) * (-1)
            }
            return -1;
        }
    }
}

이와 관련하여 다음과 같이 요약할 수 있다.

static void sort(Object[] a)  : 객체 배열에 저장된객체가 구현한 Comparable에 의한 정렬(오름차순)
static void sort(Obejct[] a, Comparator c)  : 지정한 Comparator에 의한 정렬 (사용자 정의 기준)

 

4. 적용

static class Food implements Comparable<Food> {
    long time;
    long order;

    public Food(long time, long order) {
        this.time = time;
        this.order = order;
    }

    @Override
    public int compareTo(Food o) {
        return (int) (this.time - o.time);
    }
}
    
    
public class Solution {
    public static void solution(){
        Collections.sort(temp, new Comparator<Food>() {
            @Override
            public int compare(Food a, Food b) {
                return (Long.compare(a.order, b.order));
            }
        });
    }   
}

정렬은 클래스 객체에 할당된 값을 기준으로 순서를 정한다. 클래스 내에 구현된 compareTo는 시간으로 할당된 값이 적은 값을 순서로 정렬되는 형태이고, Collections.sort의 정렬 기준인 compare는 시간에 부여된 인덱스 값을 비교하는 형태다. 로직의 흐름 중에 정렬을 지속적으로 포함시켜야 하는 경우, 위 메서드 구현이 요긴하게 쓰일 거 같다.
 
참고문헌 : 자바의 정석(3rd Edition), JAVA 11 API docs(https://docs.oracle.com/en/java/javase/11/docs/api/java.base)

Comments