Comparable의 유일무이한 메서드인 compareTo 메서드
1. equals 메서드와 다른점
// - Object의 메서드가 아니라 Comparable의 메서드이다.
// - 단순 동치성 비교에 더해 순서까지 비교할 수 있으며 제네릭하다.
// (Comparable을 구현했다는 것은 클래스의 인스턴스들에 자연적인 순서가 존재함을 의미한다.)
// - 따라서, 정렬이 가능하다.
// - 검색, 극단값 계산, 자동 정렬되는 컬렉션관리 가능
// (TreeSet, TreeMap, Collections, Array)
// "알파벳, 숫자 등과 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자"
2. compareTo의 일반 규약
// - 이 객체와 주어진 객체의 순서를 비교한다.
// - 이 객체가 주어진 객체보다 작다면 음수(-1), 같으면 0, 크다면 양수(1)를 반환한다.
// - 비교 불가능하다면, 클래스의 타입이 다르다면 ClassCastException 처리를 한다.
// -> sgn(x.compareTo(y) == -sgn(y.compareTo(x)))
// -> x.compareTo(y) > 0 && y.compareTo(z) => x.compareTo(z) > 0
// -> x.compareTo(y) == 0 => x.compareTo(z) == y.compareTo(z)
// -> x.compareTo(y) == 0 => x.equals(y)
// (equals와 마찬가지로 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면 compareTo 규약을 지키지 못한다.)
// (우회 방법 : 독립된 클래스를 만들고, 이 클래스에 원래 클래스의 인스턴스를 가리키는 필드를 둔다. 이 후, 내부 인스턴스를 반환하는 뷰 메서드를 둔다.)
// (HashSet은 equals 비교 TreeSet은 compareTo 비교)
BigDecimal instance1 = new BigDecimal("1.0");
BigDecimal instance2 = new BigDecimal("1.00");
HashSet set1 = new HashSet();
TreeSet set2 = new TreeSet();
set1.add(instance1);
set1.add(instance2);
set2.add(instance1);
set2.add(instance2);
System.out.println(set1.size());
System.out.println(set2.size());
3. compareTo 작성 요령
// - compareTo 메서드에서 관계연산자 <, >를 사용하는 것은 오류를 발생하니 더이상 추천하지 않는다.
// - 박싱된 기본타입 클래스들에 새로 추가된 정적 메서드인 compare을 이용한다.
char a = 'a';
char b = 'b';
System.out.println(Character.compare(a, b));
System.out.println(Integer.compare(1, 2));
System.out.println("a".compareTo("b"));
// - implements Comparable<Class> 를 작성시 해당 클래스와만 비교한다고 명시한다.
// - 객체 참조 필드를 비교하려면 compareTo 메서드를 재귀적으로 호출한다.
// - 핵심필드가 여러개라면 가장 핵심적인 필드부터 순차적으로 비교해간다.
public class BasicInfo implements Comparable<BasicInfo>{
private String name;
public BasicInfo(String name){
this.name = name;
}
public int compareTo(BasicInfo other){
return this.name.compareTo(other.name);
}
}
public class Info implements Comparable<Info>{
private BasicInfo basicInfo;
private int age;
public Info(BasicInfo basicInfo, int age){
this.basicInfo = basicInfo;
this.age = age;
}
public int compareTo(Info info){
int result = this.basicInfo.compareTo(info.basicInfo);
if(result == 0){
return Integer.compare(this.age, info.age);
}
return result;
}
}
// - Comparable을 구현하지 않은 필드라면 비교자(Comparator)를 사용한다.
// - 비교자를 연쇄적으로 생성해 비교가 가능하지만 약간의 성능저하가 뒤따른다.
// - 메서드 종류 : comparingInt 등 기본타입 비교, comparing으로 객체 참조 비교
public class Info implements Comparable<Info>{
private BasicInfo basicInfo;
private int age;
// 비교자 활용
private static final Comparator<Info> COMPARATOR = Comparator
.comparing((Info a) -> a.basicInfo)
.thenComparing(a -> a.age);
public Info(BasicInfo basicInfo, int age){
this.basicInfo = basicInfo;
this.age = age;
}
public int compareTo(Info info){
// 비교자 사용
return COMPARATOR.compare(this, info);
}
}
https://github.com/mokjaemin/EffectiveJAVA
'Language > Java Plus' 카테고리의 다른 글
Effective JAVA - Item16 : public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라. (1) | 2023.10.05 |
---|---|
Effective JAVA - Item15 : 클래스와 멤버의 접근 권한을 최소화하라. (0) | 2023.10.05 |
Effective JAVA - Item13 : clone 재정의는 주의해서 진행하라 (0) | 2023.09.26 |
Effective JAVA - Item12 : toString을 항상 재정의하라. (0) | 2023.09.19 |
Effective JAVA - Item11 : equals를 재정의하려거든 hashcode도 재정의하라. (0) | 2023.09.18 |