Effective Java - Item 14
Updated:
Effective Java
Item 14. Comparable을 구현할지 고려하라
Comparable
Comparable 인터페이스는 compareTo 메서드를 재정의 함으로써, 해당 객체들을 손쉽게 정렬 할 수 있다.
따라서 Comparable을 구현한 객체들의 배열은 Arrays.sort(arr)
과 같이 정렬이 가능하다.
비교를 활용하는 클래스의 예로는 정렬된 컬렉션인 TreeSet
과 TreeMap
,
검색과 정렬 알고리즘을 활용하는 유틸리티 클래스인 Collections
와 Arrays
가 있다.
public interface Comparable<T>{
int compareTo(T t);
}
compareTo 메서드의 일반 규약
- 이 객체가 주어진 객체보다 작으면 음의 정수를, 같으면 0을, 크면 양의 정수를 반환한다.
- 이 객체와 비교할 수 없는 타입의 객체가 주어지면 ClassCastException을 던진다.
- 대칭성 : 두 객체 참조의 순서를 바꾸어 비교해도 같은 결과가 나온다.
- 추이성 : 첫 번째가 두 번째보다 크고, 두 번째가 세 번째보다 크면 첫번째는 세번째보다 크다.
- 일관성 :크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다.
- compareTo 메서드로 수행한 동치성 테스트의 결과가 equals와 같아야 한다.
주의 사항
- compareTo 메서드에서 필드를 비교할 때는
Type.compare
메소드를 사용하라.
관계 연산자인 < , > 을 사용하는 것은 오류를 유발하니 지양한다. - 필드를 어느 것을 먼저 비교하느냐가 중요하다.
가장 핵심적인 필드부터 비교한다. 비교 결과가 0이 면, 그 다음으로 중요한 필드를 비교한다. - Comparable을 구현하지 않은 필드나 표준이 아닌 순서로 비교해야 한다면 비교자(Comparator)를 대신 사용한다.
위 방식을 사용하면 여러 필드를 연쇄 방식으로 비교자를 생성해 비교할 수 있다.
그리고 이 비교자를 Comparable 인터페이스가 원하는 compareTo 메서드를 구현하는데 활용할 수 있다.
대신 약간의 성능이 저하된다.
private static final Comparator<SampleClass> COMPARATOR =
comparingInt((SampleClass sc)->sc.first)
.thenComparingLong(sc.second)
.thenComparingDouble(sc.third);
public int compareTo(SampleClass sc){
return COMPARATOR.compare(this, sc);
}
- 안티 패턴 : 값의 차를 기준으로 비교
값의 차이를 기준으로 첫 번째 값이 두 번째 값보다 작으면 음수를, 같으면 0을 크면 양수를 반환하는 메서드를 만들면 안된다.
static Comparator<Object> hashCodeOrder = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return o1.hashCode() - o2.hashCode();
}
}
이 방식은 정수 오버플로를 일으키거나, 오류를 낼 수 있다.
정리
순서를 고려해야 하는 값 클래스를 작성한다면 꼭 Comparable 인터페이스를 구현한다.
이를 통해 인스턴스들을 쉽게 정렬하고, 검색하고, 비교 기능을 제공하는 컬렉션과 어우러 지도록 한다.
< 와 > 연산자는 쓰지 말아야 한다. 그 대신 박싱된 기본 타입 클래스가 제공하는 정적 compare 메서드나 Comparator 인터페이스가 제공하는 비교자 생성 메서드를 사용한다.
출처
Joshua Bloch. Effective Java 3/E. n.p.: 인사이트, 2018년 11월 1일.