티스토리 뷰
앞에서 equals 메소드의 오버라이딩으로 비교와 검색을 할 수 있음을 보았다. 그런데 두 객체에 대한 비교가 같다/같지않다로 할 수도 있지만 정렬을 하고 싶다면 크다, 같다, 작다의 세 가지 경우를 구분할 수 있어야 한다. C의 strcmp가 이러한 비교 함수의 원조다. strcmp는 두 char* 포인터를 받아서 앞의 것이 작으면 -1, 같으면 0, 크면 1을 돌려준다. 반환형은 int다. 자바에서 이런 비교를 해주는 메소드가 compareTo다.
compareTo는 많은 라이브러리 클래스에 이미 구현되어 있는데, 대표적인 예로 역시 String 클래스를 살펴보자.
String str1 = scan.next();
String max = str1;
while (scan.hasNext()) {
str1 = scan.next();
if (str1.compareTo(max) > 0)
max = str1;
}
값의 비교로 최대인 스트링을 찾는 while 루프를 위와 같이 작성할 수 있다. 여기서 스트링의 비교에서 크다와 작다는 사전순으로 순서를 정한다. 순서를 정하는 규칙은 다음과 같다.
- 기호(숫자도 아니고 알파벳도 아닌 것)이 먼저 나온다
- 숫자가 다음으로 나온다
- 알파벳이 그 다음으로 나온다. (대문자 먼저)
- 한글이나 다른 언어 알파벳이 유니코드 순서로 나온다
compareTo가 이러한 비교를 해주는데, 이것을 이용하여 ArrayList 같은 여러 개의 요소를 포함한 클래스들이 요소를 정렬하거나 최대값을 찾는 등의 일을 할 수 있다. 다음 코드로 ArrayList의 정렬을 테스트해 볼 수 있다. 여기서 Collections.sort는 ArrayList 클래스(와 기타 많은 컬렉션 클래스 객체)에 대해 정렬을 해주는 제너릭 메소드다. 이 때 이 컬렉션에 포함된 요소 클래스가 제공한 compareTo를 이용한다. 여기서는 String 클래스가 제공하는 compareTo를 이용해서 정렬을 해준다. (요소 클래스가 compareTo를 갖지 않으면 sort에서 컴파일 오류가 발생한다)
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("ABC");
list.add("123");
list.add("...");
list.add("가나다");
System.out.println(list);
Collections.sort(list); // String이 제공한 compareTo를 이용해서 정렬
System.out.println(list);
// 출력
// [abc, ABC, 123, ..., 가나다]
// [..., 123, ABC, abc, 가나다]
어떤 클래스에든 compareTo 메소드가 있으면 비교와 정렬 기능을 사용할 수 있다. 이전의 포스트에서 살펴보았던 우리의 Student 클래스에 대해 compareTo 메소드를 추가해 보자. 역시 여기서도 순서를 어떻게 정할 것인가를 클래스 작성자가 결정해야 한다. 우리는 이름, 학년, 생년월일(dob) 순으로 정렬을 하려고 한다.
@Override
public int compareTo(Student other) {
if (!name.equals(other.name))
return name.compareTo(other.name);
if (year > other.year)
return 1;
else if (year < other.year)
return -1;
return dob.compareTo(other.dob);
}
여기서도 앞의 equals나 toString과 마찬가지로 @Override를 써주고 public으로 정해진 시그너처에 따라 메소드를 정의해야 한다. 이 예에서 사용된 비교 로직은 다음과 같다.
- 이름이 this가 크면 1, this의 이름이 other의 이름보다 작으면 -1을 반환한다.
- 이름이 같으면 학년을 비교해서 역시 this가 크면 1, this가 작으면 -1을 반환한다.
- 이름과 학년이 모두 같으면 생년월일(dob)을 비교하여 큰지, 같은지, 작은지 반환하면 된다.
그런데 compareTo를 오버라이드하여 정의할 때 주의해야 할 사항이 있다. 이 메소드는 Object에 정의된 것이 아니라 Comparable이라는 인터페이스에 정의되어 있다. 그리고 위의 코드에서 매개변수를 Student로 받고 있음을 확인해 보자. 즉 equals의 예처럼 Object로 매개변수로 받아서 해당 클래스로 캐스팅을 하는 것이 아니라 매개변수를 아예 이 클래스와 같은 타입으로 받게 된다. 이것이 어떻게 가능할까?
이것이 가능한 이유는 Comparable이 제너릭이라는 타입을 정해줄 수 있는 방식으로 정의된 인터페이스이기 때문이다. ArrayList<Student>에서 꺽쇠 안에 클래스 이름을 줄 수 있는 것과 같은 원리다. 여기서는 Comparable<Student>라고 쓸 수 있다. ArrayList의 요소인 Student 클래스가 이 인터페이스를 implements해야만 그 리스트에 대해 Collections.sort를 쓸 수 있다. (클래스 선언부에 명시되어야 함)
public class Student implements Comparable<Student> {
}
컴파일러는 Collections.sort의 매개변수를 받으면 그 요소타입이 자기자신의 타입으로 Comparable을 구현했는지 확인해 본다. 그런 경우에만 sort가 가능하기 때문에 그렇지 않으면 컴파일 오류를 일으키게 된다.
Comparable 인터페이스는 그것을 구현하는 클래스를 <> 안에 타입 매개변수로 주게 된다. 자기자신과의 비교를 제공하는 것이 compareTo 메소드의 목적이다.
'자바 프로그래밍' 카테고리의 다른 글
코드 리뷰 ABC - 반복문 코드 개선 (0) | 2019.01.12 |
---|---|
코드 리뷰 ABC - if 문 줄이기 (6) | 2019.01.11 |
toString과 출력 이야기 (0) | 2019.01.10 |
equals와 비교/검색 이야기 (0) | 2019.01.10 |
프로그래밍 스타일 - 들여쓰기와 스페이스 (0) | 2019.01.07 |
- Total
- Today
- Yesterday
- sort key
- C++ 클래스
- python exercise
- Iterator
- rust
- 패턴
- follow
- 이터러블
- 지연계산
- 자바regex
- typedef
- python example
- contains
- contentEquals
- max
- format
- 스트링 +
- TypeError
- 콜렉션
- 동적바인딩
- ToString
- zip
- 이터레이터
- comparable
- 스트링
- APPEND
- indexof
- CompareTo
- Lazy evaluation
- Camel Style
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |