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() 메서드는 정렬 기준 상 o1과 o2 의 앞뒤 관게를 비교해 양의 정수, 0, 음의 정수 중 하나를 반환함
▪
어떤 값에 대한 정렬 기준은 숫자라면 더 크거나, 작음을, 문자열이라면 사전상 앞이거나 뒤 등이 될 수 있음
◦
어떤 정렬 알고리즘이 되었든 특정 두 값을 비교해서 무엇이 더 앞서는지 판단하는 논리 연산이 반드시 포함됨
◦
여기서 comparator의 compare() 메서드는 매개변수로 특정 두 값에 대한 논리 연산을 수행하여 양의 정수, 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를 구현한 객체인 c의 compare()에 의해 논리연산이 이루어지고 있음. 객체 c가 arr[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
복사
•
위의 정렬 예시에서 Comparator의 compare() 대신 Comparable의 compareTo()를 쓴다면 다음처럼 쓸 수 있을 것임
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 인터페이스를 구현해 직접 정렬 기준을 만들 수 있음