Java에서 Comparable과 Comparator는 객체를 정렬하거나 비교할 때 주로 사용하는 인터페이스이다. 둘 다 "두 객체의 순서를 비교한다"는 목적은 같지만, 비교 기준을 어디에 정의하느냐가 다르다.
🧩 Comparable
Comparable은 객체 자신이 기본 정렬 기준을 갖도록 만드는 인터페이스다.
클래스가 Comparable<T>를 구현하고 compareTo()를 오버라이드한다.
public interface Comparable<T> {
int compareTo(T other);
}
compare()는 다음 규칙으로 값을 반환한다.
- 음수: 현재 객체가 비교 대상보다 작음
- 0: 두 객체가 같음
- 양수: 현재 객체가 비교 대상보다 큼
아래 예시는 나이를 기준으로 오름차순/내림차순 정렬하는 것이다.
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
List<Person> people = new ArrayList<>();
people.add(new Person("민수", 30));
people.add(new Person("준수", 25));
people.add(new Person("철수", 28));
// 오름차순 정렬
Collections.sort(people);
System.out.println(people); // [준수(25), 철수(28), 민수(30)]
// 내림차순 정렬
Collections.sort(people, Collections.reverseOrder());
System.out.println(people); // [민수(30), 철수(28), 준수(25)]
🧩 Comparator
Comparator는 객체 외부에서 비교 기준을 별도로 정의하는 인터페이스다.
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparable과는 달리 대상 클래스 자체를 수정하지 않아도 되고, 여러 정렬 기준을 만들기에 적합하다.
// 이름 기준 정렬
Comparator<Person> nameComparator = new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
};
Lambda를 사용하면 더 간단하게 표현할 수 있다.
Comparator<Person> nameComparator = (p1, p2) -> p1.getName().compareTo(p2.getName());
people.sort(nameComparator);
// 또는
// people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
System.out.println(people); // [민수(30), 지수(25), 철수(28)]
Java 8+ 에서는 Comparator.comparing()을 쓰는 방식을 사용하기도 한다.
// 이름 기준 정렬
people.sort(Comparator.comparing(Person::getName));
// 나이 기준 정렬
people.sort(Comparator.comparingInt(Person::getAge));
// 나이 기준 내림차순 정렬
people.sort(Comparator.comparingInt(Person::getAge).reversed());
여러 조건으로 정렬하기
- compareTo()
// 나이 오름차순, 나이가 같으면 이름 오름차순
people.sort((p1, p2) -> {
int ageCompare = Integer.compare(p1.getAge(), p2.getAge());
if (ageCompare != 0) {
return ageCompare;
}
return p1.getName().compareTo(p2.getName());
});
// 나이 내림차순, 이름 오름차순
people.sort((p1, p2) -> {
int ageCompare = Integer.compare(p2.getAge(), p1.getAge());
if (ageCompare != 0) {
return ageCompare;
}
return p1.getName().compareTo(p2.getName());
});
- compareTo() + Lambda
// 나이 오름차순, 이름 오름차순
people.sort((p1, p2) -> p1.getAge() != p2.getAge()
? Integer.compare(p1.getAge(), p2.getAge())
: p1.getName().compareTo(p2.getName()));
// 나이 내림차순, 이름 오름차순
people.sort((p1, p2) -> p1.getAge() != p2.getAge()
? Integer.compare(p2.getAge(), p1.getAge())
: p1.getName().compareTo(p2.getName()));
- comparingInt()
// 나이 오름차순, 나이가 같으면 이름 오름차순
people.sort(
Comparator.comparingInt(Person::getAge)
.thenComparing(Person::getName)
);
// 나이 내림차순, 이름 오름차순
people.sort(
Comparator.comparingInt(Person::getAge)
.reversed()
.thenComparing(Person::getName)
);
🧩 Comparable vs Comparator
| 구분 | Comparable | Comparator |
| 위치 | 비교 대상 클래스 내부 | 클래스 외부 |
| 메서드 | compareTo(T o) | compare(T o1, T o2) |
| 정렬 기준 | 기본 1개 | 여러 개 가능 |
| 클래스 수정 | 필요 | 필요X |
| 사용 목적 | 자연 정렬 | 상황별/custom 정렬 |
| 예시 | 문자열 사전순, 숫자 크기순 | 이름순, 가격순, 날짜순 등 |
Comparable을 사용하는 경우
객체에 명확한 기본 정렬 기준이 있을 때 사용한다.
- String: 사전순
- Integer: 숫자순
- LocalDate: 날짜순
- 회원 객체: 회원 번호순
- 상품 객체: 상품 ID순
class Product implements Comparable<Product> {
private long id;
@Override
public int compareTo(Product other) {
return Long.compare(this.id, other.id);
}
}
Comparator를 사용하는 경우
정렬 기준이 여러 개이거나 상황에 따라 달라질 때 사용한다.
- 회원을 이름순 / 가입일순 / 포인트순으로 정렬
- 상품을 가격순 / 판매량순 / 평점순으로 정렬
- 외부 라이브러리 클래스처럼 수정할 수 없는 클래스 정렬
- 화면 또는 API 요청 파라미터에 따라 정렬 방식 변경
'CS > Java' 카테고리의 다른 글
| [Java]Lambda 와 Stream (0) | 2026.07.31 |
|---|---|
| [Java]Generics (0) | 2026.07.26 |
| Java로 멀티 쓰레드 찍먹하기 (0) | 2026.02.21 |