[ compare to ]
- Comparable 인터페이스의 유일무이한 메서드
- Object 메서드가 아니다!
- 단순 동치성 뿐만 아니라, 순서까지 비교할 수 있다. (즉, Comparable을 구현한 클래스의 인스턴스는 자연적 순서가 생긴다.)
Comparable을 구현한 객체의 정렬 방법
: Arrays.sort(객)
- 자바 플랫폼 라이브러리의 모든 값 클래스와 열거 타입이 Comparable을 구현했다.
- 순서가 명확한 값 클래스를 작성한다면 Comparable interface를 구현해야 한다.
[ 일반 규약 ]
해당 객체와 주어진 객체의 순서를 비교한다.
객체가 매개변수로 들어온 객체보다 작으면 음의 정수(-1), 같으면(0), 크다면 양의 정수(+1)을 반환
객체와 매개변수로 들어온 객체의 타입이 다르다면 ClassCastException
1. x.compareTo(y) == -y.compareTo(x)
: 두 객체 참조의 순서를 바꿔 비교해도 예상한 결과가 나와야 한다
즉, x.compareTo(y)가 1이라면 y.compareTo(x)는 -1이여야 하는 것.
2.(x.compareTo(y) > 0 && y.compareTo(z) > 0) == x.compareTo(z) > 0
: 추이성을 보장해야 한다.
3. x.compareTo(y) == 0 == (x.compareTo(z) == y.compareTo(z))
4. (x.compareTo(y) == 0) == x.eqauls(y)
: 동치성을 보장해야 한다.
즉, 크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다. 필수가 아니지만, 꼭 지키는것이 좋다.
- equals 규약과 같이 반사성, 대칭성, 추이성을 만족해야 한다.
- 기존클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가한다면, compareTo 규약을 지킬 수 없다.
- 해결 방법 : 클래스를 확장하는 대신 독립된 클래스를 만들고, 원래 클래스의 인스턴스를 가리키는 필드를 만든다. 그 후 내부 인스턴스를 반환하는 뷰 메서드를 제공하는 방식으로 compareTo를 구현하면 된다.
- compareTo 규약을 지키지 못한다면, 비교를 활용하는 클래스와 호환되지 않는다.
(TreeSet, TreeMap, Collections, Arrays 등의 정렬,검색 등을 사용할 수 없음)
HashSet : 데이터를 중복 저장할 수 없음.입력 순서를 보장하지 않음
TreeSet : 중복된 데이터를 저장할 수 없음. 입력 순서대로 데이터를 저장하지 않음. 기본적으로. 오름차순으로 데이터 정렬
LinkedHashSet : 중복된 데이터를 저장할 수 없음. 입력된 순서대로 데이터 저장.
[ compareTo method 작성 요령 ]
- equals와 비슷하지만 차이점 존재.
- Comparable은 타입을 인수로 받는 제네릭 인터페이스이다. compareTo 메서드의 인수 타입은 컴파일 타임에 정해진다.
- 즉, 입력 인수의 타입을 확인하거나 형변환할 필요가 없다. (타입이 잘못되면 어짜피 컴파일 안됨)
- compareTo 메서드는 각 필드가 동치인지를 비교하는게 아니라, 순서를 비교한다.
- 객체 참조 필드를 비교하려면 compareTo 메소드를 재귀적으로 호출한다.
- Comparable을 구현하지 않은 필드나 표준이 아닌 순서로 비교하고 싶다면 비교자 Comparator 사용.
public class item14 {
public static void main(String[] args) {
Students[] student = new Students[4];
student[0] = new Students("Ga", 20221234, 99.5);
student[1] = new Students("Jong", 20221212, 99.6);
student[2] = new Students("Dae", 20221357, 99.8);
student[3] = new Students("Sung", 20224321, 99.7);
Arrays.sort(student);
System.out.println(Arrays.toString(student));
System.out.println("---------------------------");
Comparator<Students> comparator = new Comparator<Students>() {
@Override
public int compare(Students s1, Students s2) {
return Double.compare(s1.Score, s2.Score);
}
};
Arrays.sort(student, comparator);
System.out.println(Arrays.toString(student));
}
public static class Students implements Comparable<Students>{
String Name;
int Id;
double Score;
public Students(String name, int id, double score) {
Name = name;
Id = id;
Score = score;
}
@Override
public int compareTo(Students o) {
return this.Id-o.Id;
}
@Override
public String toString() {
return "Students{" +
"Name='" + Name + '\'' +
", Id=" + Id +
", Score=" + Score +
'}';
}
}
}
주의 ! compareTo 메서드에서 <, >를 사용하는 방식은 거추장스럽고 오류를 유발한다.
[ Comparator ]
비교자를 직접 만들거나 자바가 제공하는 것을 쓰자.
- 객체 참조용 비교자를 활용하는 경우
: 단순히 필드 하나에 대해 compare로 비교하기 어려운 경우.
private static final Comparator<Students> COMPARATOR
= Comparator.comparingInt((Students student) -> student.Id).
thenComparingDouble((Students student) -> student.Score).
thenComparing((Students student) -> student.Name);
@Override
public int compareTo(Students o) {
return COMPARATOR.compare(this, o);
}
위와 같이 비교자 생성 메소드를 통해 비교연산을 다시 할 수 있다.
해시 코드를 통해 해시 코드 값의 차를 기준으로 비교할 수도 있음.
[잘못된 코드]
- 오버플로우를 일으킬 수 있다. 부동소수점 계산 방식에 따른 오류도 일어날 수 있음.
//추이성을 위배하는 코드. 쓰지 마라.
private static final Comparator<Students> HASHCODE_COMPARATOR = new Comparator<Students>() {
@Override
public int compare(Students o1, Students o2) {
return o1.hashCode() - o2.hashCode();
}
};
[올바른 코드]
//1.
private static final Comparator<Students> HASHCODE_COMPARATOR = new Comparator<Students>() {
@Override
public int compare(Students o1, Students o2) {
return Integer.compare(o1.hashCode(), o2.hashCode());
}
};
//2.
private static final Comparator<Students> HASHCODE_COMPARATOR = Comparator.comparingInt(
Object::hashCode);
핵심 정리
순서를 고려해야 하는 값 클래스를 작성하려면 Comparable interface를 구현하자.
<, >연산자는 사용하지 말자.
박싱된 기본 타입 클래스가 제공하는 정적 compare 메소드나 Comparator 인터페이스가 제공하는 비교자 생성 메소드를 이용해보자.
'JAVA > Effective Java' 카테고리의 다른 글
item 16. public 클래스에서는 접근자 메서드를 사용하라 (0) | 2022.04.25 |
---|---|
item 15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2022.04.23 |
item 13. clone 재정의는 주의해서 진행하라 (0) | 2022.04.23 |
item 12. toString을 항상 재정의하라 (0) | 2022.04.19 |
item 11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2022.04.19 |
댓글