14. Comparable을 구현할지 고려하라
1. Comparable 인터페이스란?
정의:
자바에서 객체 간의 순서를 정의하고 비교하는 데 사용되는 인터페이스
public interface Comparable<T> { int compareTo(T o); }compareTo(T o)메서드:Comparable인터페이스의 유일한 메서드.객체의 순서를 비교하여 정렬과 같은 작업에 활용
Object의equals와 유사하지만, 순서 비교 및 제네릭 타입 지원에서 차이.제네릭(
Comparable<T>)을 사용하여 타입 안정성 보장.
2. Comparable 구현의 이점
자연스러운 순서: 클래스의 인스턴스에 자연스러운 순서 부여.
쉬운 정렬:
Arrays.sort(a);와 같이 간단하게 배열 정렬 가능.다양한 활용: 검색, 극단값 계산, 자동 정렬 컬렉션(TreeSet, TreeMap) 등에서 활용.
제네릭 알고리즘 및 컬렉션과의 호환: 수많은 제네릭 알고리즘과 컬렉션을 활용 가능.
Arrays.sort(): 배열을 정렬하는 제네릭 알고리즘
Collections.sort(): 리스트를 정렬하는 제네릭 알고리즘
TreeSet: 정렬된 집합을 유지하는 컬렉션
TreeMap: 정렬된 키-값 쌍을 유지하는 컬렉션
이러한 알고리즘과 컬렉션들은 Comparable를 구현한 객체들을 자동으로 정렬하거나 순서를 유지하도록 사용.
Java 플랫폼 라이브러리 활용:
대부분의 값 클래스와 열거 타입이
Comparable구현.Integer, String, Date, Enum 등
3. compareTo 메서드 일반 규약 (equals 규약과 유사)
주어진 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환한다.
비교할 수 없는 타입의 객체가 주어지면 ClassCastException을 던진다
반사성:
모든 x에 대해
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))x.compareTo(y)가 예외를 던지면,y.compareTo(x)도 예외를 던져야 함.
추이성:
(x.compareTo(y) > 0 && y.compareTo(z) > 0)이면x.compareTo(z) > 0
대칭성:
x.compareTo(y) == 0이면sgn(x.compareTo(z)) == sgn(y.compareTo(z))
(권고) 일관성:
(x.compareTo(y) == 0) == (x.equals(y))꼭 지켜야 하는 것은 아니지만, 지키지 않을 경우 "주의: 이 클래스의 순서는 equals 메서드와 일관되지 않다"라고 명시해야 함.
참고:
sgn(표현식): 표현식의 값에 따라 -1, 0, 1을 반환하는 부호 함수.compareTo는 타입이 다른 객체에 대해ClassCastException을 던져도 됨 (대부분 그렇게 함).다른 타입 간 비교는 객체들이 구현한 공통 인터페이스를 통해 이루어질 수 있음.
4. compareTo 규약 미준수 시 문제점
hashCode규약 미준수 시 해시 기반 컬렉션(HashMap, HashSet)과 문제 발생.compareTo규약 미준수 시 비교 기반 컬렉션/클래스(TreeSet, TreeMap, Collections, Arrays)와 문제 발생.
5. 기존 클래스 확장 시 compareTo 규약 문제
문제: 기존 클래스를 확장하여 새로운 값 컴포넌트를 추가하면
compareTo규약을 지키기 어려움.Point클래스를 상속받아ColorPoint클래스를 만들고color필드를 추가할 경우,Point와ColorPoint간의 비교가 모호해짐
해결책: 상속 대신 컴포지션 사용.
독립된 클래스를 만들고, 원래 클래스의 인스턴스를 가리키는 필드를 추가.
내부 인스턴스를 반환하는 '뷰' 메서드 제공.
결과: 새로운 compareTo 작성이 가능, 클라이언트는 원래 클래스처럼 사용 가능
6. compareTo와 equals의 일관성 (권장)
권장:
compareTo의 결과와equals의 결과가 같도록 구현하는 것이 좋음.일관성 유지 시:
compareTo로 정렬된 순서와equals의 결과가 일관됨.일관성 미유지 시: 정렬된 컬렉션(TreeSet, TreeMap)에서 예상치 못한 동작 발생 가능성.
정렬된 컬렉션은
equals대신compareTo를 사용하기 때문.BigDecimal
compareTo는 수치적인 값만을 비교하지만, equals는 수치적인 값과 scale(소수점 자리수)까지 비교합니다.
예를 들어, BigDecimal("5.0")과 BigDecimal("5.00")은 compareTo로는 같지만, equals로는 다릅니다.
hashSet vs treeSet
7. compareTo 메서드 작성 요령
타입 안정성:
Comparable은 제네릭 인터페이스이므로 인수 타입은 컴파일 타임에 결정.타입 확인, 형변환 불필요.
잘못된 타입은 컴파일 오류 발생.
null입력 시NullPointerException발생.
필드 비교:
객체 참조 필드:
compareTo재귀 호출.Comparable미구현 필드, 표준이 아닌 순서:Comparator사용 (직접 만들거나, 자바 제공Comparator사용).
핵심 필드 우선 비교: 여러 핵심 필드가 있다면 가장 핵심적인 필드부터 비교.
비교 결과가 0이 아니면 즉시 반환, 0이면 다음 필드 비교.
Java 8 이후 비교자 생성 메서드 활용:
Comparator인터페이스의 비교자 생성 메서드(comparingInt, thenComparingInt 등)를 이용해 메서드 연쇄 방식으로 비교자 생성.정적 임포트와 함께 사용하면 코드가 간결해짐, 남용시 약간의 성능 저하 가능성 있음.
comparingInt: 객체에서 int 키를 추출하여 비교thenComparingInt: 이전 비교 결과가 같을 때, 추가로 int 키를 추출하여 비교 (연쇄 호출 가능)
다양한 보조 생성 메서드:
comparingLong,comparingDouble,comparing,thenComparing등.
값의 차를 이용한 비교 지양:
정수 오버플로, 부동소수점 오류 발생 가능성.
정적
compare메서드나Comparator의 비교자 생성 메서드 사용 권장.관계연산자
<,>사용 지양
핵심 정리
Comparable 인터페이스의 정의와 목적:
객체 간의 순서를 비교하기 위해 제네릭 타입을 활용하여 타입 안전성을 보장
compareTo 메서드가 순서 비교의 기준
구현의 이점:
자연스러운 정렬, 간편한 배열 정렬, 다양한 컬렉션과 제네릭 알고리즘에서의 활용이 가능
자바 라이브러리와의 호환성이 뛰어남.
compareTo 메서드의 규약:
반사성, 추이성, 대칭성, 그리고 (권고되는) equals와의 일관성을 유지하는 것이 중요함.
규약을 어길 경우 해시 기반 및 정렬 기반 컬렉션에서 문제 발생 가능성
상속과 비교 규약 문제:
기존 클래스를 확장할 때 새로운 값 컴포넌트를 추가하면 compareTo 규약을 지키기 어려워질 수 있으므로, 컴포지션을 통해 해결하는 방안을 제시함.
작성 요령 및 주의사항:
제네릭을 통해 타입 검증을 수행하고, 잘못된 타입은 컴파일 오류를 유도.
필드 비교 시 핵심 필드를 우선으로 하고, 재귀적 비교 혹은 Comparator 사용을 권장.
숫자 차이 연산 대신 Integer.compare와 같은 안전한 비교 방법 또는 Comparator의 메서드 체인을 활용.
Last updated