Search
Duplicate

[Java] Comparator, Comparable이란?

Last update: @12/23/2022

Comparator와 Comparable

둘 다 객체 정렬에 필요한 정렬기준을 제공하는 메서드를 정의한 인터페이스임

Comparator

어떤 객체에 내장된 정렬 기준을 바꿀 수 없는데 다른 기준을 사용하고 싶을 때 정렬 기준을 정의하는 방법임
Comparator 인터페이스를 보면 메서드는 아래 두 개가 전부임(주석 포함 약 500줄짜리 클래스)
package java.util; import java.io.Serializable; import java.util.function.Function; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import java.util.function.ToDoubleFunction; import java.util.Comparators; @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); 이하 생략... }
Java
복사
Comparator를 구현한 객체는 int compare(T o1, T o2) 메서드를 반드시 가짐
compare() 메서드는 정렬 기준o1o2 의 앞뒤 관게를 비교해 양의 정수, 0, 음의 정수 중 하나를 반환함
어떤 값에 대한 정렬 기준은 숫자라면 더 크거나, 작음을, 문자열이라면 사전상 앞이거나 뒤 등이 될 수 있음
어떤 정렬 알고리즘이 되었든 특정 두 값을 비교해서 무엇이 더 앞서는지 판단하는 논리 연산이 반드시 포함됨
여기서 comparatorcompare() 메서드는 매개변수로 특정 두 값에 대한 논리 연산을 수행하여 양의 정수, 0, 음의 정수를 반환함
정렬 알고리즘은 compare()의 결괏값을 통해 정렬을 수행함
가장 만만한 선택 정렬을 예로 들면
public void sort(Object[] arr, Comparator c) { // arr는 정렬 대상, c는 비교기준 for(int i = 0; i < arr.length - 1; i++) { for(int j = i + 1 ; j < arr.length; j++) { Object tmp = null; if(c.compare(arr[i], arr[j]) > 0) { tmp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }
Java
복사
Comparator를 구현한 객체인 ccompare()에 의해 논리연산이 이루어지고 있음. 객체 carr[i]arr[j]를 받아서 compare() 메서드를 통해 arr[j]arr[i]보다 앞에 와야 한다고 계산하면 양수를 반환할 것이고, 정렬 알고리즘은 arr[j]의 위치를 arr[i]와 바꿀 것임

Comparable

Comparable은 숫자나 문자열처럼 다른 객체와의 순서를 정할 수 있는 모든 객체들이 구현하는 인터페이스
compareTo() 메서드를 통해 객체 본인(this)과 인수로 받은 다른 객체를 비교해서 양수, 0, 음수를 반환하는데, 이는 객체 자체에 객체 스스로가 어떻게 정렬되어야 하는지 초기(default) 기준을 정의해 놓은 것임
Comparable 인터페이스 코드를 보면 아래가 전부임. 객체 본인과 스스로를 비교해서 정수를 반환하는 compareTo() 메서드를 구현해야 한다는 뜻.
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
Java
복사
예를 들어 Integer 클래스의 compareTo() 메서드를 보면 아래처럼 생김. Integer 객체는 기본적으로 본인(왼쪽)이 큰 숫자면 양수를 반환하도록 정렬 기준을 정의해놓음
public int compareTo(Integer anotherInteger) { return compare(this.value, anotherInteger.value); } public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); }
Java
복사
위의 정렬 예시에서 Comparatorcompare() 대신 ComparablecompareTo()를 쓴다면 다음처럼 쓸 수 있을 것임
public void sort(Object[] arr) { // arr는 정렬 대상 for(int i = 0; i < arr.length - 1; i++) { for(int j = i + 1 ; j < arr.length; j++) { Object tmp = null; if(arr[i].compareTo(arr[j])) > 0) { tmp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }
Java
복사
Arrays.sort() 메서드는 기본적으로 compare()이나 compareTo()가 양수면 자리바꿈을 하기 때문에 정수는 오름차순 정렬이 됨

Comparator.reverseOrder()를 통한 내림차순 정렬

아래처럼 내림차순 정렬을 할 때 Comparator.reverseOrder() 메서드를 사용하게 되는데,
Arrays.sort(arr, Comparator.reverseOrder());
Java
복사
소스를 쫓아가다 보면 ReverseComparator 클래스를 마주치게 됨
Collections.ReverseComparator 클래스의 compare() 메서드를 보면 c1.compareTo(c2)c2.compareTo(c1)처럼 단순히 순서만 바꾼 것을 볼 수 있음
private static class ReverseComparator implements Comparator<Comparable<Object>>, Serializable { ... public int compare(Comparable<Object> c1, Comparable<Object> c2) { return c2.compareTo(c1); } private Object readResolve() { return Collections.reverseOrder(); } @Override public Comparator<Comparable<Object>> reversed() { return Comparator.naturalOrder(); } }
Java
복사
따라서 반환하는 정수의 부호가 바뀌어 정렬 기준도 바뀌게 되는 것임

그래서 어떤 것을 써야할까?

위에서 언급했듯이, Integer 등 임의로 수정할 수 없는 객체에 대해 기본 정렬 기준 이외의 정렬 기준이 필요할 때 compatator 인터페이스를 구현해 직접 정렬 기준을 만들 수 있음

References

관련 문서