Comparator 와 Comparable
Java 에서 Collections API 를 통해서 List, Map, Set 등등 의 자료구조를 쉽게 사용할 수 있다.
종종 해당 Collections 에 있는 Data 를 정렬해야 할일이 있는데 어떻게 정렬되는지 한번 알아보도록 하자
Comparable
Oracle 문서에 나와있는 정의는 아래와 같다.
This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.
간략하게 이야기 하면 객체의 자연적인 순서(숫자로 치면 1, 2, 3 ... 과 같은) 를 참조하여 정렬한다고 생각하면 될 것 같다.
더 나와 있는 설명을 보면 Collections.sort 또는 Arrays.sort 를 이용하여 정렬할때 해당 Object Type 이 구현한 Comparable 을 이용해서 정렬한다고 한다. 그렇다면 아래 코드는 어떻게 정렬할 수 있을까?
위의 사진의 코드를 실행시켜 보면 아래와 같은 오류를 마주한다.
java.lang.ClassCastException: class com.example.javaplayground.SortExample$Person
cannot be cast to class java.lang.Comparable
이 에러가 나는 이유는 무엇일까? 우리는 우리가 만든 Object 의 Natural Order 를 정의해주지 않았다. 즉, 기존에 있던 문자열이나 숫자 등은 우리가 기본적으로 아는 자연적인 순서가 존재하는데 (예를 들면 사전 순서라던지) 해당 Type 의 Comparable 이 구현되어 있지 않기 때문에 어떤 순서로 정렬해야 되는지 모르기 때문에 나는 예외이다.
Comparable 구현
우리는 "나이가 어린 사람이 제일 앞" 을 자연적인 순서라고 생각하고 코드를 작성해보자.
기본적으로 아래 compareTo 를 보면 int 를 리턴한다. 따라서 정수의 자연적인 순서를 기본적으로 이용해야 하므로 우리가 원하는 순서로 정렬하기 위해서는 -1 을 곱해주어야 한다.
아래 처럼 결과를 보면 잘 정렬된 것을 확인할 수 있다.
Comparator
Comparator 의 Oracle 정의는 아래와 같습니다.
Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order.
쉽게 해석해보자면 Comparable 과 같이 sort 를 하기 위함인데, 요건 natural order 가 아니라 정렬 순서가 뭔가 복잡해서 커스텀하게 정렬하려고 할때 필요할 것 같다고 느껴진다. 일단 사용하기 위해서 기본적인 정렬 순서를 알아야 하는데 compareTo 가 정렬하는 방식은 아래와 같다.
- o1 이 o2 보다 크다면 양수를 반환한다.
- o1 이 o2 보다 작다면 음수를 반환한다.
- o1 이 o2 와 같다면 0을 반환한다.
예시를 좀 더 알맞게 쓰기 위해 Person Class 의 Property 를 조금만 더 추가해보자.
위의 클래스에 "bornAge" 는 "태어날때 시작나이" 를 뜻해서, 우리가 정렬하고 싶은 순서는 "bornAge 가 작은 사람이 일단 우선적이고, 같다면 나이를 비교하여 나이가 적은 사람이 앞으로 온다" 로 정렬하고 싶다고 해보자.
처음에 bornAge 를 통해서 제일 먼져 비교하고, 만약 같다면 age 로 비교하는 코드이다. Comparator 는 기존의 Class 에 구현 하는 것이 아니라 다른 Class 로 보통 분리해서 사용한다. 기존 Class 에 넣어서 정렬할때는 위에서 이야기한 Comparable 을 이용하는 것이 좀 더 좋다.
위의 PersonComparator 는 Arrays.sort 에 list 와 함께 Comparator 를 구현하는 Concrete 를 넣어주면 된다.
위의 코드를 실행시키면 아래와 같은 결과가 산출되는 것을 확인할 수 있다.