14. Comparable 을 구현할지 고려하라
순서를 고려해야 하는 값 클래스를 작성 → 꼭 Comparable 인터페이스를 구현
그 인스턴스들을 쉽게 정렬하고, 검색하고, 비교 기능을 제공하는 컬렉션과 어우러지도록 해야한다.
#
compareTocompareTo
Comparable 인터페이스의 유일한 메서드
- Object 메서드가 아님
- Object equals 와 유사함
#
compareTo 특징#
1. compareTo 는 단순 동치성 비교 + 순서 비교Object equals 와 유사하지만 compareTo는 순서까지 비교한다.
#
2.Comparable 을 구현한 클래스- 객체는 자연 순서 존재
- 객체들의 배열은
Arrays.sort(a);
로 정렬 가능
#
3. Comparable 을 구현하면 이 인터페이스를 활용하는 수 많은 제네릭 알고리즘과 컬렉션의 효과를 사용할 수 있음- 자바 플랫폼 라이브러리의 모든 값 클래스와 열거타입34이 Comparable 을 구현함.
- 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자
- 검색, 극단값 계산, 자동 정렬되는 컬렉션 관리 가능
아래 코드는 String 이 Comparable 을 구현하였기 때문에 → 명령줄 인수들을 알파벳 순으로 출력함
info
HashSet
- 데이터를 중복 저장할 수 없고 순서를 보장하지 않음
TreeSet
- 중복된 데이터를 저장할 수 없고 입력한 순서대로 값을 저장하지 않음
- TreeSet은 기본적으로 오름차순으로 데이터를 정렬함
LinkedHashSet
- 중복된 데이터를 저장할 수 없고 입력된 순서대로 데이터를 관리함
#
compareTo 일반 규약equals 규약과 유사함
이 객체와 주어진 객체의 순서를 비교한다.
- 이 객체가 주어진 객체보다
- 작으면 음의정수 반환
- 같으면 0 반환
- 크면 양의정수 반환
- 이 객체와 비교할 수 없는 타입의 객체가 주어지면
- ClassCastException 을 던짐
sng(표현식) 표기
부호 함수(signum function)
- 표현식의 값이 음수, 0, 양수 일 때 -1, 0, 1 을 반환하도록 정의됨
Comparable 을 구현한 클래스는
- 모든 x, y 에 대해
sng(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
이다
- 모든 z 에 대해
x.compareTo(y) == 0
이면sgn(x.compareTo(z)) == sgn(y.compareTo(z))
- 필수는 아니지만 꼭 지키는게 좋은 항목
(x.compareTo(y) == 0) == (x.equals(y))
여야 한다.- compareTo 로 수행한 동치성 테스트의 결과가 equals 와 같아야 한다.
- 권고를 지키지 않는 클래스
- "주의: 이 클래스의 순서는 equals 메서드와 일관되지 않다." 명시해야 함
#
equals 와 compareTo#
equals- 모든 객체에 대해 전역 동치관계를 부여함
- 타입 캐스팅 필요
#
compareTo- 타입이 다른 객체를 신경쓰지 않아도 됨
- 타입이 다른 객체가 주어지면 ClassCastException 을 던지면 됨.
- 규약에서는 다른 타입 비교도 허용하지만,
보통 비교할 객체들이 구현한 공통 인터페이스(Collections, Set, Map)를 매개로 이뤄진다.
- hashCode 규약을 지키지 못하면 해시를 사용하는 클래스와 어울리지 못하듯, compareTo 규약을 지키지 못하면 비교를 활용하는 클래스와 어울리지 못한다.
- 비교를 활용하는 클래스
- 정렬된 컬렉션 TreeSet, TreeMap
- 검색과 정렬알고리즘을 활용하는 Collections, Arrays
- 해당 컬렉션이 구현한 인터페이스(Collections, Set, Map..) 에 정의된 동작과 어울리지 못함.
- 이 인터페이스들은 equals 메서드의 규약을 따른다고 되어 있지만, 동치성을 비교할 때 compareTo 를 사용함
- 비교를 활용하는 클래스
compareTo 와 equals 가 일관되지 않는 BigDecimal 클래스
- HashSet 인스턴스를 생성한 다음
new BigDecimal("1.0")
,new BigDecimal("1.00")
을 추가한다.- equals 메서드로 비교하면 서로 다르기 때문에 hashSet 은 원소를 2개 갖는다.
- TreeSet 인스턴스를 사용하면 원소 1개 만 갖는다.
- compareTo 메서드로 비교하면 두 bigDecimal 인스턴스가 같다.
#
Comparable 을 구현한 클래스를 확장해 값을 추가하는 방법- 확장(상속)하여 값을 추가할 때 compareTo 규약을 지킬 방법이 없다.
- 컴포지션을 사용하자
#
compareTo 메서드 작성 요령==
연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.instanceof
연산자로 입력이 올바른 타입인지 확인한다.- 입력을 올바른 타입으로 형변환한다.
- 입력 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치하는지 하나씩 검사한다.
#
equals와 차이점- Comparable 은 타입을 인수로 받는 제네릭 인터페이스
- compareTo 메서드의 인수 타입은 컴파일 타임에 정해짐
- 입력 인수타입을 확인하거나 형변환 할 필요가 없다.
- null 을 인수로 넣어 호출하면 NullPointerException 을 던져야 한다.
#
객체 compareTo 의 비교compareTo 는 각 필드의 동치를 비교하는 것이 아니라, 순서를 비교함
- 객체참조 필드를 비교하려면 → compareTo 메서드를 재귀적으로 호출함
- Comparator 비교자의 사용
- Comparable 을 구현하지 않은 필드
- 표준이 아닌 순서로 비교해야 할 때
- 비교자는 직접 만들거나 자바가 제공하는 것 중에 고르면 된다.
- 다음 코드는 자바가 제공하는 비교자를 사용하였음
implements Comparable<CaseInsensitiveString>
- CaseInsensitiveString 의 참조는, CaseInsensitiveString 참조와만 비교할 수 있다
#
기본 타입의 비교자바7 이후의, 박싱된 기본 타입 클래스들에 추가된 정적 메서드 compare 사용
#
비교 순서클래스에 핵심필드가 여러개라면 비교 순서가 중요하다
#
1. 가장 핵심적인 필드부터 비교- 비교 결과가 0 이 아니라면, (순서가 결정되면) 결과를 바로 반환해야 한다.
- 핵심 필드가 똑같다면(0), 똑같지 않은 필드를 찾을 때 까지 그 다음으로 중요한 필드를 비교한다.
#
비교자 생성 메서드를 활용한 비교자자바8 에서, Comparator 인터페이스가 비교자 생성 메서드 comparator constructor method 와 메서드 연쇄방식으로 비교자를 생성할 수 있게 되었다.
#
특징- 약간의 성능저하
- 정적 비교자 생성 메서드들을 이름으로만 사용할 수 있어 코드가 깔끔해짐