객체 비교 및 정렬에 자주 쓰이지만 헷갈리는 개념인 Comparable 과 Comparator 의 개념을 확실하게 정리해보자. 🫡
우선 Comparable 과 Comparator 는 모두 인터페이스이다. 즉, 이를 사용하기 위해서는 인터페이스 내에 선언된 메소드를 반드시 구현해야 한다는 것 !
Comparable : compareTo(T o) Comparator : compare(T o1, T o2)Java.lang 패키지에 있어 import 해줄 필요가 없다. Student 클래스를 비교해보자. 우선 Student 클래스에 Comparable 을 implement 하고, compareTo 메소드를 구현해야 할 것이다.
먼저 나이 를 기준으로 비교해본다고 가정하자. 즉, 자기 자신의 나이를 기준으로 매개변수로 들어온 상대방인 o의 나이의 값을 비교하면 된다 !
코드는 다음과 같다.
class Student implements Comparable<Student> {
int age; // 나이
int classNumber; // 학급
Student(int age, int classNumber) {
this.age = age;
this.classNumber = classNumber;
}
// 필수 구현 부분
@Override
public int compareTo(Student o) {
// 자기 자신의 age가 o의 age보다 크다면 양수 (자리 바뀜)
if(this.age > o.age) {
return 1;
}
// 자기 자신의 age와 o의 age가 같다면 0
else if(this.age == o.age) {
return 0;
}
// 자기 자신의 age가 o의 age보다 작다면 음수 (자리 그대로)
else {
return -1;
}
}
}
compareTo 에서 대소 관계 파악의 과정은 자기 자신을 기준으로 상대방 (매개변수) 과의 차이를 리턴하는 것으로, 다음의 그림을 보면 이해가 잘 될 것이다.

이를 활용해 주로 1, 0, -1 로 표현한다.
즉, 양수가 리턴되면 자리가 바뀌는 것 !
그렇다면 정수가 아닌 두 값의 차를 바로 반환해 번거로운 조건식을 없애볼 수 있을 것이다.
코드는 다음과 같다.
class Student implements Comparable<Student> {
int age; // 나이
int classNumber; // 학급
Student(int age, int classNumber) {
this.age = age;
this.classNumber = classNumber;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
Java.util 패키지에 있어 import 가 필요하다. 위와 마찬가지로 Student 객체를 비교해보는데, 이번에는 학급 을 기준으로 해보자.
Comparable 과는 다르게 두 객체를 비교하는 것이므로 o1 과 o2 의 학급을 비교해주는 것이다. 즉, 객체 자체와는 상관 없이 독립적으로 매개변수로 넘겨진 두 객체를 비교하는 것이 포인트 !
import java.util.Comparator; // import 필요
class Student implements Comparator<Student> {
int age; // 나이
int classNumber; // 학급
Student(int age, int classNumber) {
this.age = age;
this.classNumber = classNumber;
}
@Override
public int compare(Student o1, Student o2) {
// o1의 학급이 o2의 학급보다 크다면 양수
if(o1.classNumber > o2.classNumber) {
return 1;
}
// o1의 학급이 o2의 학급과 같다면 0
else if(o1.classNumber == o2.classNumber) {
return 0;
}
// o1의 학급이 o2의 학급보다 작다면 음수
else {
return -1;
}
}
}
마찬가지로 두 객체의 차이를 바로 반환해볼 수 있다.
코드는 다음과 같다.
class Student implements Comparator<Student> {
int age; // 나이
int classNumber; // 학급
Student(int age, int classNumber) {
this.age = age;
this.classNumber = classNumber;
}
@Override
public int compare(Student o1, Student o2) {
return o1.classNumber - o2.classNumber;
}
}
2차원 배열을 바로 Arrray.sort()를 통해 정렬하려고 하면 java.lang.ClassCastException: I cannot be cast to java.lang.Comparable 오류가 발생한다. 원인은 비교 기준이 구현되어 있지 않기 때문에 캐스팅에 실패했기 때문 !
compare() 메서드를 Override 하여 원하는 정렬 기준 명시 int[][] arr = new int[][]{{5,40},{3,50},{1,30},{4,20},{2,10}};
// 1. Comparator 익명 클래스 구현
Arrays.sort(arr, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0]; // 첫번째 숫자 기준 오름차순 {1,30}{2,10}{3,50}{4,20}{5,40}
//return o2[0] - o1[0]; // 첫번째 숫자 기준 내림차순 {5,40}{4,20}{3,50}{2,10}{1,30}
//return o1[1] - o2[1]; // 두번째 숫자 기준 오름차순 {2,10}{4,20}{1,30}{5,40}{3,50}
//return o2[1] - o1[1]; // 두번째 숫자 기준 내림차순 {3,50}{5,40}{1,30}{4,20}{2,10}
}
});
// 다중 조건
int[][] arr2 = new int[][]{{5,40},{3,50},{1,30},{4,20},{2,10},{6,40},{6,50},{6,10},{6,20},{6,30}};
Arrays.sort(arr2, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] != o2[0] ? o1[0] - o2[0] : o1[1] - o2[1]; // 첫번째 기준 오름차순, 같을 땐 -> 두번째 기준 오름차순 : {1,30}{2,10}{3,50}{4,20}{5,40}{6,10}{6,20}{6,30}{6,40}{6,50}
//return o1[0] != o2[0] ? o1[0] - o2[0] : o2[1] - o1[1]; // 첫번째 기준 오름차순, 같을 땐 -> 두번째 기준 내림차순 : {1,30}{2,10}{3,50}{4,20}{5,40}{6,50}{6,40}{6,30}{6,20}{6,10}
}
});
// 다중 조건 (if문 사용, 의미는 위와 같음.)
Arrays.sort(arr2, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[0] == o2[0]) { // 첫번째 기준 오름차순, 같을 땐 -> 두번째 기준 오름차순 : {1,30}{2,10}{3,50}{4,20}{5,40}{6,10}{6,20}{6,30}{6,40}{6,50}
return o1[1] - o2[1];
}
else { // 첫번째 기준 오름차순, 같을 땐 -> 두번째 기준 내림차순 : {1,30}{2,10}{3,50}{4,20}{5,40}{6,50}{6,40}{6,30}{6,20}{6,10}
return o2[1] - o1[1];
}
});
람다식을 활용해 더욱 더 간결하게 표현 가능int[][] arr = new int[][]{{5,40},{3,50},{1,30},{4,20},{2,10}};
// 2. Lambda 사용 - Java 8이상
Arrays.sort(arr, (o1, o2) -> {
return o1[0] - o2[0]; // 첫번째 숫자 기준 오름차순 {1,30}{2,10}{3,50}{4,20}{5,40}
});
💡 참고한 글