-
아이템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