Comparable
인터페이스는 안에 compareTo
함수를 가지고 있는 interface 로 이를 구현하면 많은 Collection들에 sort 함수를 인자로 받아 사용할 수 있어서 유용하다.
간단하게 구현방식은 아래와 같다. (아래는 책에서 권장하지 않는 방식이었다..)
class Test implements Comparable<T> {
int a;
int b;
@Overrides
public int compareTo(T other){
if(this.a < other.a) return 1;
else if(this.a > other.a) return -1;
else if(this.b > other.b){
}
}
}
Comparable
인터페이스의 compareTo()
메서드는 equals
메서드와 다른 두가지 특징을 가지고 있다.
Comparable
를 구현했다는 것은 그 클래스의 인스턴스들은 순서가 있음을 의미그래서 Comparable을 구현한 클래스는 간편하게 정렬이 가능하다.
Arrays.sort(a);
아래는 내가 코딩테스트를 대비할 때 사용했던 실제 코드이다.
class Node implements Comparable<Node>{
public int x;
public int y;
public int index;
public Node left;
public Node right;
public Node parent;
public Node(int a, int b, int c)
{
x = a;
y = b;
index = c;
}
@Override
public int compareTo(Node a)
{
if(this.x > a.x)
return 1;
else
return -1;
}
}
class Solution {
public void dfs(int current, int start, int end)
{
...
}
public int[][] solution(int[][] nodeinfo) {
int[][] answer = new int[2][nodeinfo.length];
nodes = new Node[nodeinfo.length];
for(int i=0; i<nodeinfo.length; i++)
nodes[i] = new Node(nodeinfo[i][0], nodeinfo[i][1], i+1);
Arrays.sort(nodes);
...
}
}
이런식으로 bfs 같은 탐색을 해야할때 유리하게 시작하거나, 여러 그래프 탐색 문제에서 정렬할 일이 많으므로 Comparable
인터페이스를 자주 구현해주곤 했다.
해당 객체와 주어진 객체의 순서를 비교하는 메서드이다. 이 객체보다 작으면 음의 정수를, 같으면 0, 크면 양의 정수를 반환한다.
그리고, 만약 서로 비교하지 못하는 클래스를 비교한다면 단순히 ClassCastException
을 던진다.
Comparable
을 구현한 클래스는 모든 x, y에 대해 sgn(x.compareTo(y)) == - sgn(y.compareTo(x))
여야 한다. (대칭성)x.compareTo(y) == 0
이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z))
다. (추이성)(x.compareTo(y)) == 0) == (x.equals(y))
여야 한다. (꼭 안지켜도 되지만 클래스의 순서가 equals 메서드와 동일하지 않다는 것을 알아야 함) public int compareTo(PhoneNumber pn) {
int result = Short.compare(areaCode, pn.areaCode);
if (result == 0) {
result = Short.compare(prefix, pn.prefix);
if (result == 0)
result = Short.compare(lineNum, pn.lineNum);
}
return result;
}
이 비교자들을 Comparable 인터페이스가 원하는 compareTo 메서드를 구현하는데 간결하게 코드를 짤 수 있다. 하지만 약간의 성능 저하가 발생하기도 한다.(책의 저자는 10%정도 느려졌다고 한다)
위의 PhoneNumber 클래스를 Comparator을 사용해서 구현한 모습이다.
private static final Comparator<PhoneNumber> COMPARATOR =
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComapringInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
return COMAPRATOR.compare(this, pn);
}
여기서 comparingInt 는 키 추출 함수(lambda로) 를 인수로 받아 그 키를 기준으로 순서를 정하는 comparator를 반환하는 정적 메서드이다.
그 다음 두번째 비교자 생성 매서드인 thenComparingInt가 수행되어 int 키 추출자 함수를 입력받아 비교자를 반환하여 계속 비교를 진행하게 된다.