Comparable과 Comparator가 필요한 이유와 이 둘의 차이점에 대해 한 번 정리해보려고 한다.
이 둘의 이름에서 유추할 수가 있듯이 뭔가 비교를 할 때 쓰이는 것 같은데 정확히 어떤 역할을 하는지 한번 알아보자.
왜 써야하는가?
우리가 가장 많이 대소 관계의 비교를 사용하는게 if문일 것이다. 아래처럼 기본 데이터 타입은 연산자를 통해 쉽게 비교가 가능하다.
if (1 > 10) {
System.out.println("참");
} else {
System.out.println("거짓");
}
그렇다면 객체는 어떻게 비교(대소 관계)해야 할까? 이 때 '객체 간의 비교는 equals로 하지 않나? 라는 의문이 들 수 있는데, 여기서 말하는 비교란 대소 관계 (a > b, a = b, a < b)를 말한다.
예를 들면, Building 이라는 클래스가 있고, 그 안에 인스턴스 변수는 아래와 같다고 하자.
private int buildingNo; //건물번호
private int area; //평수
private String name; //건물명
private String address; //주소
Building a와 Building b 둘 중에 건물번호가 더 높으면 크다고 할지, 평수가 크면 크다고 할지 알 수가 없다. 그래서 비교할 기준을 정하고 그 기준으로 비교하기 위해 Comparable과 Comparator를 사용한다.
(더 나아가서는 정렬을 할 때도 사용한다. 실제로 Arrays.sort() 메서드 안에 mergesort() 메서드를 보면 Comparator를 사용하는 것을 알 수 있다. 정렬과의 관계는 추후에 정리해보려고 한다.)
왜 써야 하는지를 알아 봤으니 이제 어떻게 쓰는지 알아보자
Comparable
Comparable과 Comparator 모두 인터페이스이다. 그래서 클래스에서 구현을 해주면 되는데, 우선 Comparable 인터페이스부터 살펴보면 아래의 사진과 같이 compareTo(T o) 라는 추상 메서드가 있는 것을 확인할 수 있다.
위에서 예를 들었던 Building 클래스에 Comparable 인터페이스를 구현을 해보도록 하자.
구현할 때 대소 관계에서 각각 어떤 값을 반환해줘야 할까?
크면(>) 1, 같으면(0), 작으면(-1)을 반환해도 되고 양수, 0, 음수를 반환해도 된다.
@Override
public int compareTo(Building o) {
return this.area - o.area;
}
나는 평수를 기준으로 비교하고자 하여 위와 같이 작성하였다.
해당 메서드를 구현할 때는 자기 자신을 기준으로 삼아야 한다. 그래서 this.area - o.area의 결과값을 return 해주도록 구현했다.
public class BuildingTest {
public static void main(String[] args) {
Building a = new Building(1, 300, "a건물", "경기도 김포시");
Building b = new Building(1, 400, "b건물", "서울시");
System.out.println("a.compareTo(b) = " + a.compareTo(b));
System.out.println("b.compareTo(a) = " + b.compareTo(a));
if (a.compareTo(b) > 0) {
System.out.println("a건물이 더 넓습니다.");
} else if (a.compareTo(b) < 0) {
System.out.println("b건물이 더 넓습니다.");
}
}
}
//실행결과
//a.compareTo(b) = -100
//b.compareTo(a) = 100
//b건물이 더 넓습니다.
위에서 말했듯이 자기 자신을 기준으로 하기 때문에 a객체, b객체 각각 메서드를 호출하면 300(a.area) - 400(b.area) /
400(b.area) - 300(a.area) 로 동작한다.
Comparator
이번에는 Comparator 인터페이스를 살펴보자. 해당 인터페이스에는 아래와 같이 compare(T o1, T o2) 추상 메서드가 선언되어 있다.
여기서 짐작해볼 수 있는게, Comparable은 compareTo(T o) = 즉 자기 자신과 매개 변수(다른 객체)를 비교하지만 Comparator는 두 매개변수(객체)를 비교한다.
@Override
public int compare(Building o1, Building o2) {
return o1.area - o2.area;
}
똑같이 Building 클래스에 위와 같이 평수를 기준으로 비교하도록 구현했다.
public class BuildingTest {
public static void main(String[] args) {
Building a = new Building(1, 300, "a건물", "경기도 김포시");
Building b = new Building(1, 400, "b건물", "서울시");
System.out.println("a.compare(a, b) = " + a.compare(a, b));
System.out.println("a.compare(b, a) = " + a.compare(b, a));
}
}
위에서 설명했듯이 두 매개변수를 비교한다고 했지만 사실 자기 자신을 넣을 수도 있고, 만약에 c라는 또 다른 인스턴스를 생성해서 c.compare(a, b) 라고 할 수 있다. 하지만 굳이 이렇게 사용하는 것 보다 compareTo() 메서드를 이용해서 두 객체를 비교하는게 더 낫지 않을까?
그래서 주로 Comparable을 통하여 기본적으로 사용하는 비교 기준을 세워두고, Comparator는 익명 객체를 통하여 그 때 그 때, 다른 기준이 필요할때 사용한다.
그렇다면 위에서 기존에 평수를 기준으로 하는 compareTo() 메서드를 구현했지만 건물명으로 비교할 경우가 생겼다고 가정하자.
Comparator<Building> comp = new Comparator<Building>() {
@Override
public int compare(Building o1, Building o2) {
return o1.getName().compareTo(o2.getName());
}
};
System.out.println(comp.compare(a, b));
//실행결과
//-1
위와 같이 Comparator<Building> 타입의 인스턴스를 생성하고, 익명 객체를 통해 건물명을 기준으로 비교하는 compare 메서드를 구현했다.
결론
- Comparable은 자기 자신과 다른 객체를 비교한다.
- Comparator는 두 매개 변수(객체)를 비교한다.
- Comparable은 기본적으로 사용하는 비교 기준을 세운다.
- Comparator는 Comparable에서 세운 기준 외에 다른 기준이 필요할 때 사용한다.
'☕️ Java' 카테고리의 다른 글
[Java] List 인터페이스 - ArrayList 클래스 (1) | 2023.05.30 |
---|---|
[Java] Collection Framework (2) | 2023.05.18 |
[Java] String, StringBuilder, StringBuffer의 차이 (0) | 2023.04.26 |
[Java] 인터페이스는 왜 다중 상속이 가능할까? (0) | 2023.04.18 |
[Java] toString()을 Override 하는 이유 (0) | 2023.04.07 |