on
아이템14. Comparable을 구현할지 고려하라
아이템14. Comparable을 구현할지 고려하라
728x90
1. Comparable 이란?
Comparable 링크
Comparable 인터페이스는 객체를 정렬화 하는 데 사용되는 인터페이스이다.
해당 인터페이스를 구현한 객체들의 List와 Array는 Collections.sort 에 의해 자동으로 정렬될 수 있다.
Comparable를 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서가 있음을 뜻한다.
public interface Comparable { public int compareTo(T o); }
자바 라이브러리의 모든 값 클래스와 열거 타입이 Comparable을 구현했다.
ex ) String
def "1. String 클래스"(){ when: Set s = new TreeSet<>(); Collections.addAll(s, "D", "B", "C") // 테스트 결과 검증 then: println s 1 == 1 } [B, C, D]
정렬을 활용하는 클래스 > TreeSet, TreeMap, Collections , Arrays 등
" 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자."
2. compareTo 일반 규약
"compareTo 반환값" 기준 객체(x)와 비교 객체(y)가 있을때 1) x < y > -1 반환 2) x = y > 0 반환 3) x > y > 1 반환
"일반 규약" Comparable 를 구현한 클래스는 모든 x, y에 대해 다음과 같은 규약을 가진다. sgn(signum function) 는 부호 함수를 의미하며 표현식의 값이 음수,0, 양수 일때 -1, 0, 1을 반환한다. 1) sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 즉, x.compareTo(y) = -1 이면 y.compareTo(x) = 1 그렇기 때문에 x.compareTo(y) 가 예외를 던질때에 한해서 y.compareTo(x)도 예외를 던져야한다. 2) 추이성 보장 x.compareTo(y) >0 && y.compareTo(z) 이면 x.compareTo(z) > 0 3)x.compareTo(y) == 0 sgn(x.compareTo(y)) == sgn(y.compareTo(x)) 4) 필수는 아니지만 지키는게 좋다. x.compareTo(y) == 0 이면 x.equals(y) 이 권고를 지키지 않으면 다음과 같은 사실을 명시하는게 좋다 " 주의: 이 클래스의 순서는 equals 메서드와 일관되지 않다 "
3. 구현 시 주의점
1) equals와 compareTo
4번 규약을 지키지 않은 경우 의도치 않은 동작을 유발할 수 있다.
def "2. compareTo vs equals"(){ when: BigDecimal bigDecimal1 = new BigDecimal("1.0"); BigDecimal bigDecimal2 = new BigDecimal("1.00"); Set hashSet = new HashSet<>(); Set treeSet = new TreeSet<>(); hashSet.add(bigDecimal1); hashSet.add(bigDecimal2); treeSet.add(bigDecimal1); treeSet.add(bigDecimal2); // 테스트 결과 검증 then: hashSet.size() == 2 treeSet.size() == 1 }
위와 같이 Bigdecimal을 HashSet과 TreeSet에 넣어 비교할 때 HashSet의 경우 equals값으로 데이터를 비교하고 TreeSet의 경우 compareTo 메서드로 비교하기 때문에 두 개의 사이즈는 다르다.
위와 같은 경우를 방지하기 위해 4번 규약을 지키는 것이 좋다.
2) compareTo 메서드에서 관계 연산자 < 와 > 사용을 지양하라.
< 와 >는 거추장스럽고 오류를 유발할 수 있어 추천하지 않는다. 대신 자바 7부터 추가된 박싱 된 기본 타입 클래스의 정적 메서드인 compare 사용을 추천한다.
전
public int compareTo (Integer y) { return x < y ? 1 : (x == y) ? 0 : -1; }
후
public int compareTo(Integer y) { return Integer.compare(x, y); }
3) hashcode의 차를 이용한 비교는 하지 않는 것이 좋다.
Bad
정수 overflow를 일으키거나 IEEE 754 부동소수점 계산방식에 따른 오류 발생 가능
static Compartor hashCodeOrder = new Comparator<>(){ public int compare(Object o1, Object o2){ return o1.hashCode() - o2.hashCode(); } }
정적 메서드나 compare 혹은 Comparator 생성 메서드를 이용해 비교해주자.
정적 메서드
public class TestCompare implements Comparable { private long height; private double weight; private int age; public TestCompare(long height, double weight, int age) { this.height = height; this.weight = weight; this.age = age; } @Override public int compareTo(TestCompare testCompare) { int result = Long.compare(height, testCompare.height); if (result == 0) { result = Double.compare(weight, testCompare.weight); if (result == 0) { return Integer.compare(age, testCompare.age); } } return result; } }
Comparator 생성 메서드 (성능이 좀 느릴 수 있음 / 타입 추론을 안 하기 때문에 타입을 지정해주어야 함)
public class TestCompare implements Comparable { private static final Comparator COMPARATOR = Comparator.comparingLong((TestCompare cc) -> cc.height) .thenComparingDouble(cc -> cc.weight) .thenComparingInt(cc -> cc.age); private long height; private double weight; private int age; public TestCompare(long height, double weight, int age) { this.height = height; this.weight = weight; this.age = age; } @Override public int compareTo(TestCompare testCompare) { return COMPARATOR.compare(this, testCompare); } }
728x90
from http://development-record.tistory.com/95 by ccl(A) rewrite - 2021-11-16 00:27:50