백엔드 개발자 블로그

[Effective Java] Item14. Comparable을 구현할지 고려하라 본문

Java

[Effective Java] Item14. Comparable을 구현할지 고려하라

backend-dev 2024. 4. 7. 20:13

Comparable 인터페이스의 compareTo 메서드를 구현하게 되면 동치성 비교와 순서 비교가 가능해집니다.

 

compareTo 메서드의 일반 규약

동치성

A == A 의 결과는 항상 true 이어야 합니다.

 

대칭성

A == B와 B == A 결과가 같아야 합니다.

 

추이성

A > B 이고 B > C 이면 A > C 이어야 합니다.


equals vs compareTo 

내부적으로 정렬하는 곳(TreeSet, TreeMap, Collections, Arrays)에는 compareTo를 사용하고, 아닌 곳에는 equals를 사용하여 동치성 비교를 합니다. 아래 예시를 봅시다.

public static void main(String[] args) {
        // 스케일이 다른 숫자를 넣는다.
        BigDecimal bd1  = new BigDecimal("1.0");
        BigDecimal bd2 = new BigDecimal("1.00");

        HashSet<BigDecimal> hset = new HashSet<>();
        TreeSet<BigDecimal> tset = new TreeSet<>();

        hset.add(bd1);
        hset.add(bd2);
        System.out.println("hset cnt is " + hset.size());

        tset.add(bd1);
        tset.add(bd2);
        System.out.println("tset cnt is " + tset.size());
}

 

 

위 코드의 결과는 아래와 같습니다.

hset cnt is 2
tset cnt is 1

 

HashSet 클래스의 경우 내부적으로 순서를 고려하기 않기 때문에 동치성 비교시 equals를 사용합니다. 즉 소수점 정보까지 모두 고려해서 같은지 비교합니다. 따라서 hashSet 은 BigDecimal이 다르다고 판단하여 2개가 나왔습니다.

TreeSet 클래스의 경우 내부적으로 정렬을 하기 때문에 동치성 비교시 compareTo를 사용합니다. 스케일 정보를 포함하지 않으므로 두 BigDecimal을 같다고 판단하여 1개가 나왔습니다.


compareTo 구현 방법

Java 7 이전 : 정수 타입은 관계연산자(>,<)로, 실수 타입은 Double.compare와 Float.compare를 사용 

Java 8 부터 : 박싱된 기본 클래스의 compare 정적 메소더 사용

// Comparator의 비교자 생성 메서드를 통해서 compareTo 메서드에 정렬 기준을 부여해줄 수 있다.

private static final Comparator<PhoneNumber> COMPARATOR =
            comparingInt((PhoneNumber pn) -> pn.areaCode) 
                    .thenComparingInt(pn -> pn.getPrefix()) 
                    .thenComparingInt(pn -> pn.lineNum);

    @Override
    public int compareTo(PhoneNumber pn) {
        return COMPARATOR.compare(this, pn);
    }
}

 

 

주의할 점

두값의 차이를 반환하는 메서드는 만들면 안됩니다. 정수 오버플로나 부도소수점 계산 방식에 따른 오류를 낼 수 있습니다.

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