Comparator의 default method chaining
Java에서 정렬을 위해서 Comparator를 사용하는데 어느 순간 다양한 static /default 메서드들이 생겨서 정렬을 훨씬 편하게 처리할 수 있게 되었다.
이 메서드들은 대부분 자기 자신을 리턴해서 chaining 형태로 사용되는데 람다식과 제네릭을 사용하면서 주의할 점이 있어서 관련 내용을 포스팅 한다.
Comparator의 static/default 메서드들
다음은 Comparator에 선언된 static 메서드 들이다.
메서드 명
|
선언부와 설명
|
naturalOrder()
|
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder()
|
Comparable 타입의 T가 가진 compareTo()에 의해 오름차순 정렬하는 Comparator를 리턴한다.
|
reverseOrder()
|
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder()
|
naturalOrder()의 역순으로 정렬하는 Comparator를 리턴한다.
|
comparing()
|
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor)
|
Function이 T를 파라미터로 받아서 Comparable 타입의 U를 리턴할 때 U를 기준으로 정렬하는 Comparator<T>를 리턴한다.
|
comparingP()
|
public static <T> Comparator<T> comparingT(ToPFunction<? super T> keyExtractor)
|
comparing()에서 U의 타입이 P일 경우 별도의 오토박싱 없이 처리하는 메서드이다.
|
nullsFirst()
|
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
|
정렬 결과 null 값을 맨 처음에 위치시킨다.
|
nullsLast()
|
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)
|
정렬 결과 null 값을 맨 나중에 위치시킨다.
|
P는 Int, Long, Double을 나타낸다.
|
메서드들에 타입 파라미터가 적용되면서 매우 복잡하게 보이고 (솔직히 복잡하다.ㅜㅜ) 한글로 설명하다 보니 말이 매우 난잡해졌다.
추가로 사용할 수 있는 default 메서드들은 아래와 같다.
메서드 명
|
선언부와 설명
|
reversed()
|
default Comparator<T> reversed()
|
현재 Comparator의 정렬 방식의 역순으로 정렬하는 Comparator를 리턴한다.
|
thenComparing()
|
default Comparator<T> thenComparing(Comparator<? super T> other)
|
1차 정렬 이후 추가적인 정렬을 위한 Comparator를 리턴한다.
|
thenComparingP()
|
default Comparator<T> thenComparingP(ToPFunction<? super T> keyExtractor)
|
비교 대상이 기본형인 경우 thenComparing() 대신 사용한다.
|
P는 Int, Long, Double을 나타낸다.
|
간단한 사용법
다음과 같은 리스트를 정렬시켜 보자.
여기서는 정렬 되지 않은 원본을 유지하기 위해서 Stream을 생성해서 Stream을 정렬시켜본다.
처음은 가장 간단한 형태로 문자열의 길이 기반으로 정렬을 처리한다.
이번에는 default 메서드인 thenComparing을 이용해서 2차 정렬을 시도해보자. 길이가 같다면 문자열의 자연 순인 알파벳 기준으로 정렬한다.
Comparator를 sorted 안으로 넣어보자!!
1회성인 Comparator가 sorted 밖으로 돌아디니는게 별로 예뻐보이지 않는다. 이를 inline 스타일로 작성해보자.
첫 번째 케이스를 inline 스타일로 변경해보면...
두 번째 녀석도 inline 스타일로 변경해보면..
당연히 깔끔하게 출력될것으로 예상했지만 슬프게도 타입을 추정할 수 없다는 오류 메시지를 토해내버린다.
원인
원인은 inline 스타일의 sorted 내부에서 사용된 첫번째 Comparator.comparing이 컴파일 되고 그걸 이어받아서 thenComparing이 동작하는데 첫번째의 연산에서 타입을 추정할 수 없기 때문에 발생한다.
해당 람다식인 str -> str.length() 만 보면 도대체 str이 뭐지??? 할 수밖에 없다.
반면 inline 스타일이 아닌 경우는 Comparator<String>이라고 선언되었기 때문에 이를 통해서 타입 추정이 이뤄지므로 상관 없게 된다.
처리 방법 1
가장 기본적인 형태는 위에서 살펴봤듯이 inline 스타일을 포기하는 것이다.
처리 방법 2
두 번째 방법은 첫 번째 메서드 호출 시 타입 파라미터를 명시해주는 것이다.
처리방법 3
마지막 방법은 메서드 참조를 이용하는 형태도 있다.