안녕하세요!
오늘 공부한 Java의 Comparable
, Comparator
에 대해서 작성해보겠습니다.
Comparable
은 인터페이스입니다.
Comparable
의 compareTo()
메소드는 객체를 정렬하는데 사용됩니다.
자바에서 같은 인스턴스를 비교하는 클래스들은 모두 Comparable
인터페이스를 구현하고 있습니다.
int[] arr = {50, 30, 10, 23, 45};
System.out.println("정렬전 : " + Arrays.toString(arr));
Arrays.sort(arr);
System.out.println("정렬후 : " + Arrays.toString(arr));
String[] women = {"김연경", "박정아", "김수지", "김희진", "이소영"};
System.out.println("정렬전 : " + Arrays.toString(women));
Arrays.sort(women);
System.out.println("정렬후 : " + Arrays.toString(women));
메인 메소드에서 위 코드를 실행시키면 어떤 결과가 도출될까요?
int
타입 배열은 물론, String
타입의 배열도 정렬됩니다.
위 사진은 String
클래스의 선언부입니다.
보시면, Comparable<String>
을 구현하고 있는 것을 확인할 수 있습니다.
compareTo()
메소드도 재정의하고 있는 것을 확인할 수 있습니다.
그렇다면, compareTo()
메소드는 무엇을 비교하는 메소드일까요?
현재 자신과 같은 타입의 객체와 비교합니다.
즉, 나와 다른 사람을 비교하는 것이겠죠. (String
의 메소드 파라미터 이름도 anotherString
이네요)
같은 타입의 다른 객체와 자신을 비교해 값을 int
타입으로 리턴합니다.
음수
면 자신이 다른 객체보다 작다, 0이면 동일하다, 양수
면 자신이 다른 객체보다 큰 것을 의미합니다.
compareTo()
메소드는 제네릭 타입의 객체를 비교할 때 사용합니다.
primitive
타입이라면 Integer, Character, Double ...
의 박싱 타입으로 오토 박싱 되어서 compareTo()
메소드를 사용할 수 있습니다.
class Student {
int no, score;
}
간단한 Student
클래스입니다.
다른 Student
객체와 score
을 기준으로 변경하고 싶다면 어떻게 해야할까요?
class Student implements Comparable<Student> {
int no, score;
@Override
public int compareTo(Student o) {
return this.score - o.score;
}
}
Comparable
인터페이스를 구현해 compareTo()
메소드를 재정의한다면, 현재 객체만의 비교 기준을 가질 수 있습니다.
하지만 객체의 비교 기준을 바꾸고 싶을 때, 클래스에 들어가서 재정의된 compareTo()
메소드를 변경해야 할까요?
String
의 compareTo()
를 오름차순이 아닌 내림차순으로 변경하고 싶다면 자바 API를 변경해야 할까요?
이 때 필요한 개념이 Comparator
인터페이스입니다.
Comparable
는 자기 자신과 다른 것 과의 비교이므로 클래스 기준에서 compareTo()
를 재정의하고 사용해야 했습니다.
하지만 Comparator
는 자기 자신과는 아무런 상관이 없고, 그저 다른 두 객체의 비교를 진행합니다.
구현해야 할 메소드인 compare()
도 매개변수가 2개입니다.
즉, 다른 두 객체를 받아서 비교만 Comparator
가 진행합니다.
비교 기준은 Comparable
과 동일하게 음수
면 자신이 다른 객체보다 작다, 0이면 동일하다, 양수
면 자신이 다른 객체보다 큰 것을 의미합니다.
클래스에 Comparator
를 구현해도 되지만, 비교 기준을 변경하고 싶다면 익명 함수나 람다로 많이 사용됩니다.
자주 사용되는 Arrays.sort()
메소드의 매개변수에도 (정렬할 객체, Comparator<T>)
가 있습니다.
정렬 기준을 Comparator
로 선언하는 것이죠
public static void main(String[] args) {
Student[] students = {
new Student(1, 10),
new Student(3, 50),
new Student(2, 80),
new Student(4, 10)
};
}
메인 메소드에 정렬할 Student
배열을 선언합니다.
static class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.score - o2.score;
}
}
위의 방법은 Comparator
인터페이스를 구현하는 클래스를 하나 생성해 정렬하는 방법입니다.
위처럼 클래스를 선언하고, 메인 메소드 안에서 Arrays.sort(students, new StudentComparator());
를 실행하면 StudentComparator
클래스에 선언된 compare
을 기준으로 정렬됩니다.
하지만, 나와는 상관없는 다른 두 객체를 비교할 예정인데 굳이 클래스를 생성할 이유는 없습니다.
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.score - o1.score;
}
});
그럴 땐 한 번 사용하고 끝나는 익명 클래스를 만들어서 사용할 수도 있습니다.
위의 예제는 Student
점수 기준 내림차순 입니다.
Arrays.sort(students, (o1, o2) -> o2.score - o1.score)
더 간단하게는 람다도 사용할 수 있습니다.
가독성 있고 코드가 단순해 Comparator
를 사용할 때는 람다를 자주 사용하는 것 같습니다.
그렇다면 점수가 같을 때, 학생 번호로 정렬하는 방법은 없을까요?
Arrays.sort(students, (o1, o2) -> (o2.score != o1.score)? o2.score - o1.score: o2.no - o1.no);
3항 연산자를 이용하면 쉽게 끝납니다.
자기 자신과, 다른 객체의 비교를 정해진 기준으로 해야한다면 Comparable<T>
의 compareTo(T o)
를 사용하고,
자기 자신과는 상관 없이 다른 두 객체의 비교를 해야하는데, 굳이 기준이 항상 같지 않다면 Comparator<T>
의 compare(o1, o2)
를 사용하면 될 것입니다.
어제의 TIL이 밀렸지만.. 오늘 TIL 먼저 작성해봤습니다!
항상 헷갈렸던 Comparable
과 Comparator
를 확실하게 이해하고 사용할 수 있을 것 같아서 너무 좋네요!!
이번 포스팅도 읽어주셔서 감사합니다 :)