티스토리 뷰

앞에서 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 메소드의 목적이다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함