아이템14. Comparable을 구현할지 고려하라
2021. 7. 25. 00:39ㆍ책/이펙티브자바
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
compareTo()
- 동치성비교
- 순서 비교
- 제네릭
Comparable 구현시
- 해당 클래스의 인스턴스들에는 자연적인 순서가 있음을 뜻한다.
- Arrays.sort(a); 와 같이 손쉽게 정렬이 가능하다.
- 자바 플랫폼 라이브러리의 모든 값 클래스와 열거타입이 Comparable을 구현했다.
- 이 인터페이스를 활용하는 수많은 제네릭 알고리즘과 컬렉션의 힘을 누릴 수 있다.
- 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 을 구현하자.
compareTo() 규약
이 객체와 주어진 객체의 순서를 비교한다. 주어진 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환한다. 이 객체와 비교할 수 없는 타입의 객체가 주어지면 ClassCastException을 던진다.
- sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
- (x.compareTo(y) > 0 && y.compareTo(z) > 0) → x.compareTo(z) > 0
- (x.compareTo(y) == 0) == (x.equals(y))
- 필수는 아니지만 지키는게 좋고, 지키지 않는다면 "주의: 이 클래스의 순서는 equals 메소드와 일관되지 않다" 와 같이 명시해줘야 한다.
정렬된 컬렉션들은 동치성을 비교할 때 equals 대신 compareTo를 사용한다.
BigDecimal number1 = new BigDecimal("1.0");
BigDecimal number2 = new BigDecimal("1.00");
Set<BigDecimal> hs = new HashSet<>();
hs.add(number1);
hs.add(number2);
Set<BigDecimal> ts = new TreeSet<>();
ts.add(number1);
ts.add(number2);
hs.size(); // 2
ts.size(); // 1
compareTo() 작성법
- compareTo 메서드에서 관계 연산자 < 와 > 를 사용하는 이전 방식은 사용을 추천하지 않는다.
- 대신 Integer.compare, Double.compare ... 과 같은 박싱된 기본 타입 클래스들의 compare 메소드 사용을 추천한다.
- 클래스에 핵심 필드가 여러 개라면 어느 것을 먼저 비교하는가가 중요하다.
- 가장 핵심적인 필드부터 비교해나가자.
// 1. 일반적인 방법
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;
}
// 2. 비교자 생성 메소드를 활용한 비교자
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);
}
- 자바8 에서는 Comparator 인터페이스가 일련의 비교자 생성 메소드와 팀을 꾸려 메소드 연쇄 방식으로 비교자를 생성할 수 있게 되었다.
- 약간의 성능 저하가 뒤따르게 된다.
- 정적 임포트 기능을 사용하면 코드가 훨씬 깔끔해진다.
두 값의 차이로 비교하는 방법
// 정적 compare 메소드를 활용한 비교자
static Comparator<Object> hashCodeOrder = new Comparator<>() {
public int compare(Object o1, Obejct o2) {
return Integer.compare(o1.hashCode(), o2.hashCode());
}
};
// 비교자 생성 메소드를 활용한 비교자
static Comparator<Object> hashCodeOrder =
Comparator.comparingInt(o -> o.hashCode());
private static final Comparator<Student> HASHCODE_COMPARATOR =
Comparator.comparingInt(Object::hashCode);
결론
- 순서를 고려해야 하는 값 클래스를 작성한다면 꼭 Comparable 인터페이스를 구현하자.
- compareTo 메소드에서 필드의 값을 비교할 때 <, > 연산자는 쓰지 말자. 대신 박싱된 기본 타입 클래스가 제공하는 정적 compare 메소드나 Comparator 인터페이스가 제공하는 비교자 생성 메소드를 사용하자.
- 두 값의 차이로 비교하는 방법을 사용하지 말자.
'책 > 이펙티브자바' 카테고리의 다른 글
아이템16. public 클래스에는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2021.07.26 |
---|---|
아이템15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2021.07.26 |
아이템13. clone 재정의는 주의해서 진행하라 (0) | 2021.07.20 |
아이템12. toString을 항상 재정의하라 (0) | 2021.07.19 |
아이템11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2021.07.02 |