[아이템 14] Comparable을 구현할지 고려하라

Jimin Lim·2022년 3월 21일
0

Effective Java

목록 보기
14/38
post-thumbnail
post-custom-banner

아이템 14

Comparable을 구현할지 고려하라

CompareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있으며 제네릭하다. Camparable을 구현했다는 것은 그 클래스의 인스턴스에 순서가 있음을 뜻한다.즉, Caparable을 구현한 객체들의 배열은 바로 정렬할 수 있다.
알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자.

compareTo 규약

기준 객체 A 가 B보다 작으면 음의 정수를, 같으면 0, 크다면 양의 정수를 반환한다. 만약 A와 비교할 수 없는 객체가 들어온다면 ClassCastException을 던진다.

  1. sgn(x.compareTo(y)) == -sgn(x.compareTo(y))
    첫 번째 객체가 두 번째 객체보다 작으면 두 번째 객체는 첫 번째 객체보다 커야 한다.
  2. x.compareTo(y) > 0 && y.compareTo(z) > 0 이면 x.compareTo(z) > 0이다.
    추이성을 보장해야 한다.
  3. x.compareTo(y) == 0이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z))
    크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다.
  4. (x.compareTo(y) == 0) == (x.equals(y)) 를 권고한다.
    compareTo 메서드로 수행한 동치성 테스트의 결과가 equals와 같아야 한다. 이를 잘 지키면 compareTo로 줄지은 순서와 equals의 결과가 같게 된다.

compareTo의 구현

기본 타입 필드가 여럿일 때의 비교자

public int compareTo(PhoneNumber pn) {
	int result = Short.compare(areaCode, pn.areaCode); // 가장 중요한 필드
    if (result == 0) {
    	result = Short.compare(prefix, pn.prefix); // 두 번째로 중요한 필드
        if (result == 0) {
        	result = Short.compare(lineNum, pn.lineNum); // 세 번째로 중요한 필드
    }
    return result;
}

여럿일 때, 어느 것을 먼저 비교하느냐가 중요해진다. 가장 핵심적인 필드부터 비교하도록 하면 된다.

비교자 생성 메서드를 활용한 비교자

private static final Comparator<PhoneNumber> COMPARATOR = 
		comparingInt((PhoneNumber pn) -> pn.areaCode)
        	.thenComparingInt(pn -> pn.prefix)
            .thenComparingInt(pn -> pn.lineNum);
            
public int compareTo(PhoneNumber pn) {
	return COMPARATOR.compare(this, pn);
}

위의 방식은 클래스를 초기화할 때 비교자 생성 메서드를 2개 이용해 비교자를 생성한다. compareingInt는 람다를 인수로 받고 이 람다는 지역 코드를 기준으로 순서를 정하는 Comparator<PhoneNumber>를 반환한다. 이후의 비교는 thenComparingInt이 수행한다.

주의할 점

해시 코드의 값의 차를 기준으로 하는 비교자느 사용하면 안된다.
정수 오버플로우를 발생시키거나 부동소수점 계산 방식에서 오류가 발생할 수 있다.

권장하는 방식

정적 compare 메서드

static Comparator<Object> hashCodeOrder = new Comparator<>() {
	public int compare(Object o1, Object o2) {
    	return Integer.compare(o1.hashCode(), o2.hashCode());
    }
};

비교자 생성 메서드

static Comparator<Object> hashCodeOrder = 
	Comparator.comparingInt(o -> o.hashCode());

핵심 정리

순서를 고려하는 값 클래스는 Comparable 인터페이스를 구현하도록 해야한다. 또한 compareTo 메서드에서는 >, < 연산자는 쓰지 말아야 한다.

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️
post-custom-banner

0개의 댓글