자바에서 Comparable과 Comparator는 모두 컬렉션의 요소를 정렬하는 데 사용되는 인터페이스입니다. 두 인터페이스는 목적이 같지만, 소속 패키지와 객체 비교 방식에서 뚜렷한 차이가 있습니다.
Comparator는 java.util 패키지에 속하며, 전달받은 두 개의 객체를 서로 비교합니다. 반면 Comparable은 java.lang 패키지에 속하며, 자기 자신(this)과 매개변수로 전달된 객체 하나를 비교합니다.
Comparable과 Comparator 핵심 차이점 비교
| 번호 | 구분 | Comparable | Comparator |
|---|---|---|---|
| 1 | 메서드 | compareTo(Object a) 메서드를 제공합니다. | compare(Object o1, Object o2) 메서드를 제공합니다. |
| 2 | 정렬 호출 | Collections.sort(List) 메서드로 Comparable 타입 객체의 컬렉션을 정렬합니다. | Collections.sort(List, Comparator) 메서드로 Comparator 타입 객체의 컬렉션을 정렬합니다. |
| 3 | 정렬 기준 | 단일 정렬 기준만 제공합니다. | 여러 개의 정렬 기준을 제공할 수 있습니다. |
| 4 | 소속 패키지 | java.lang 패키지에 속합니다. | java.util 패키지에 속합니다. |
Comparable 예제
아래 예제는 Laptop 클래스가 Comparable<Laptop> 인터페이스를 구현하여, RAM 용량을 기준으로 노트북 목록을 오름차순으로 정렬하는 코드입니다. compareTo() 메서드 내부에서 현재 객체(this.ram)와 비교 대상 객체(o.getRam())를 직접 비교한다는 점에 주목하세요.
public class ComparableExample {
public static void main(String[] args) {
List<Laptop> laptopList = new ArrayList<>();
laptopList.add(new Laptop("HCL", 16, 800));
laptopList.add(new Laptop("Apple", 8, 100));
laptopList.add(new Laptop("Dell", 4, 600));
Collections.sort(laptopList);
for (Laptop lap : laptopList) {
System.out.println(lap.getRam());
}
}
}
public class Laptop implements Comparable<Laptop> {
String name;
int ram;
int price;
public Laptop(String name, int ram, int price) {
super();
this.name = name;
this.ram = ram;
this.price = price;
}
public String getName() {
return name;
}
public int getRam() {
return ram;
}
public void setRam(int ram) {
this.ram = ram;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int compareTo(Laptop o) {
if (this.ram > o.getRam())
return 1;
else {
return -1;
}
}
}
실행 결과
4 8 16
Comparator 예제
다음 예제는 Comparator 인터페이스를 활용한 정렬 방법입니다. compare() 메서드는 비교 대상인 두 객체(o1, o2)를 매개변수로 받으며, 람다 표현식을 사용하면 이름 기준 정렬처럼 새로운 정렬 규칙을 기존 코드 수정 없이 간단히 추가할 수 있습니다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Laptop implements Comparator {
String name;
int ram;
int price;
public Laptop(String name, int ram, int price) {
super();
this.name = name;
this.ram = ram;
this.price = price;
}
public String getName() {
return name;
}
public int getRam() {
return ram;
}
public void setRam(int ram) {
this.ram = ram;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int compare(Laptop o1, Laptop o2) {
if (o1.getRam() < o2.getRam()) {
return -1;
} else if (o1.getRam() > o2.getRam()) {
return 1;
} else {
return 0;
}
}
public static void main(String[] args) {
List laptopList = new ArrayList<>();
laptopList.add(new Laptop("HCL", 16, 800));
laptopList.add(new Laptop("Apple", 8, 100));
laptopList.add(new Laptop("Dell", 4, 600));
Comparator com = (Laptop o1, Laptop o2) -> o1.getName().compareTo(o2.getName());
Collections.sort(laptopList, com);
for (Laptop lap : laptopList) {
System.out.println(lap.getName());
}
}
}
실행 결과
Apple Dell HCL
어떤 것을 선택해야 할까?
두 인터페이스 중 무엇을 사용할지는 개발 상황에 따라 달라집니다.
- Comparable: 클래스의 기본 정렬 순서(natural ordering)를 하나만 정의하면 될 때 적합합니다. 나이순, 이름순처럼 고정된 기준으로 정렬하는 경우에 사용합니다.
- Comparator: 여러 가지 정렬 기준이 필요하거나, 소스 코드를 수정할 수 없는 외부 라이브러리 클래스를 정렬해야 할 때 유용합니다. 람다 표현식과 함께 사용하면 정렬 로직을 유연하게 교체할 수 있습니다.