ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Item 14: Consider implementing Comparable
    독서/Effective Java 2021. 12. 12. 18:35

    이 장에서 설명하는 다른 메서드와 달리 compareTo 메서드는 Comparable 인터페이스의 유일한 메소드로, 단순 동치성 비교와 순서 비교를 허용하고 제네릭하다는 점을 제외하면 Object의 equals 메서드와 특성이 비슷하다.

    Comparable을 구현함으로써 클래스는 해당 인스턴스에 자연스러운 순서가 있음을 의미한다.

    Comparable을 구현하는 객체 배열을 정렬하는 것은 다음과 같이 간단하다.

    Arrays.sort(a);

    자바 플랫폼 라이브러리의 모든 값 클래스와 열거타입이 Comparable을 구현했고, 이 인터페이스를 활용하는 수많은 제네릭 알고리즘과 컬렉션의 힘을 누릴 수 있다.

     

    compareTo() 규약

    1. 이 Object와 주어진 Object의 순서를 비교한다. 
      • 주어진 Object보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환한다. 
      • 이 Object와 비교할 수 없는 타입의 Object가 주어지면 ClassCastException을 던진다.
    2. 대칭성을 보장해야 한다.
      • 모든 x 및 y에 대해 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))인지 확인해야 한다.
      • y.compareTo(x)가 예외를 throw하는 경우에는 x.compareTo(y)도 예외를 던져야 한다.
    3. 관계가 전이적인지 확인해야 한다.
      1. (x.compareTo(y) > 0 && y.compareTo(z) > 0)일 경우 x.compareTo(z) > 0로 나와야 한다.
    4. x.compareTo(y) == 0이 모든 z에 대해 sgn(x.compareTo(z)) == sgn(y.compareTo(z))를 지키는 지 확인해야 한다.
    5. (필수 아님) (x.compareTo(y) == 0) == (x.equals(y))이 보장되어야 한다.

     

    Equals 규약(항목 10)과 마찬가지로 이 규약은 그리 복잡하지는 않다. 모든 Object에 전역 동등 관계를 부과하는 equals 메소드와 달리, compareTo는 다른 유형의 객체에 걸쳐 작동할 필요가 없다.

     

    작성 방법

    • compareTo 메서드에서 관계 연산자 '<', '>' 를 사용하는 방식은 추천하지 않는다. 대신 Integer.compare, Double.compare ... 과 같은 Boxed wrapper class들의 compare 메소드 사용을 추천한다.
    • 객체 참조 필드를 비교하려면 compareTo 메서드를 재귀적으로 호출한다.
    • 클래스에 핵심 필드가 여러 개라면 어느 것을 먼저 비교하는가가 중요하다.(SQL에서의 order by 순서처럼...)
    // Multiple-field Comparable with primitive fields
    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;
    }
    • 가장 핵심적인 필드부터 비교해나가자.
    • 자바8 에서는 Comparator 인터페이스가 일련의 비교자 생성 메소드와 팀을 꾸려 메소드 연쇄 방식으로 비교자를 생성할 수 있게 되었다.
    // Comparable with comparator construction methods
    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);
    }
    • 약간의 성능 저하가 뒤따르게 된다.
    • 정적 임포트 기능을 사용하면 코드가 훨씬 깔끔해진다.
    • 두 값의 차이를 이용한 정적 메소드 사용 방법은 다음과 같다. 

     

    // Comparator based on static compare method
    static Comparator<Object> hashCodeOrder = new Comparator<>() {
        public int compare(Object o1, Obejct o2) {
            return Integer.compare(o1.hashCode(), o2.hashCode());
        }
    };
    // Comparator based on Comparator construction method
    static Comparator<Object> hashCodeOrder = 
        Comparator.comparingInt(o -> o.hashCode());

     

     

    댓글

Designed by Tistory.