자료구조를 공부하다보면 필연적으로 정렬에 대해 배우게 됩니다. 예를 들어 TreeSet 은 내부에 BST(Binary Search Tree)가 구현되어 있는데, 이때 어떤 것을 기준으로 어떻게 정렬할 것인지에 대한 정의가 되어 있어야 정상적으로 동작이 됩니다. String과 Integer 같은 클래스들은 Comparable 인터페이스를 이미 구현하고 있어서 TreeSet<String> 인 경우에 오름차순(사전편찬순) 정렬이 수행됩니다.
TreeSet 클래스 활용하기
- 객체의 정렬에 사용하는 클래스
- Set 인터페이스를 구현하여 중복을 허용하지 않고, 오름차순이나 내림차순으로 객체를 정렬할 수 있음
- 내부적으로 이진검색트리(BST)로 구현됨
- BST에 저장하기 위해 각 객체를 비교해야 함
- 비교 대상이 되는 객체에 Comparable이나 Comparator 인터페이스를 구현해야 TreeSet에 추가 될 수 있음
- String, Integer 등 JDK의 많은 클래스들이 이미 Comparable을 구현하고 있음
위의 TreeSetTest.java 에서는 String 타입을 전달 받는 TreeSet 을 선언하고 문자열 "홍길동", "강감찬", "이순신" 을 추가하고 있습니다. 일반적인 Set 자료구조는 중복을 허용하지 않고, 추가된 객체를 꺼낼 때 순서를 보장하지 않는 것이 일반적입니다. 하지만 TreeSet 은 내부적으로 정렬이 수행되어 꺼낼 때 순서가 정해지는데, 이때 정렬 기준과 방식을 String 클래스가 구현하고 있는 Comparable 인터페이스의 함수인 CompareTo() 가 제공합니다. 그 방법은 기준이 되는 값과 비교할 대상의 크기를 비교해서 기준이 되는 값이 크면 양수, 같으면 0, 비교할 대상이 크면 음수를 반환하도록 CompareTo() 함수를 정의하면 오름차순이 되고, 그 반대로 정의할 경우 내림차순이 됩니다. String 클래스는 기본적으로 오름차순 방식을 택하고 있습니다. 따라서 TreeSetTest.java 의 출력 결과는 "강감찬", "이순신", "홍길동" 순으로 나오게 됩니다.
그렇다면 내부적으로 Comparable 인터페이스를 구현하고 있지 않은 경우는 어떻게 해야 할까요?
Member.java 에서는 필드 변수로 int memberId; 와 String name; 을 가지고 있고, 생성자를 통해서 두 변수를 받아 객체를 만듭니다. Member 객체를 생성하고, TreeSet<Member> 에 전달했지만 결과는 에러가 발생합니다. 그 이유는 Member 를 어떤 것을 기준으로 어떻게 정렬할 지 정해지지 않았기 때문입니다. 따라서 Member 에서 Comparable 인터페이스를 구현하여 정렬 기준을 만들어 줘야 합니다.
Comparable 인터페이스를 구현한 Member 를 해석해보면, 정렬 기준은 'memberId'가 되고, 정렬 방식은 '내림차순'이 됩니다. 여기서 만약 Member의 'name'을 기준으로 '오름차순' 정렬하고 싶다면, return this.name-member.name; 로 바꾸면 됩니다.
그럼 마지막으로 Comparator 인터페이스에 대해 알아보겠습니다. 대부분의 경우에 Comparable 인터페이스만으로 정렬을 수행할 수 있습니다. 하지만 이미 Comparable 인터페이스가 구현되어 있는 경우, Comparator 인터페이스로 정렬 방식을 재구현할 수 있습니다.
TreeSet<String> 을 정의하고 문자열을 추가하면 기본적으로 오름차순 정렬이 된다는 것을 위에서 이미 확인했습니다. 내림차순으로 바꾸고 싶다면 String 클래스가 구현하고 있는 Comparable 인터페이스의 compareTo() 함수를 재정의해야 하는데, 그건 불가능합니다. String 클래스는 final class 이기 때문에 상속받을 수 없고, 결국 불변(Immutable)입니다. 이때 유용하게 사용할 수 있는 것이 Comparator 인터페이스입니다. 위의 코드처럼 Comparator<String> 을 구현하는 클래스를 생성하고, Comparator 인터페이스가 가진 함수 compare() 을 정의하면 됩니다. Comparable 인터페이스의 CompareTo() 와 다른 점은, 파라미터를 2개 받고 있다는 것입니다. 하지만 하나는 기준점이고 나머지 하나는 비교할 대상으로 생각하면 compareTo() 와 똑같이 사용할 수 있습니다.
따라서 위에서 정의한 compare() 를 해석해보면, s1을 기준점, s2를 비교할 대상으로 한 s1.compareTo(s2) 의 결과는 String 내부에 정의된 compareTo() 함수 그대로인 오름차순입니다. 여기에 -1 을 곱한 결과를 return 하고 있으므로 compare() 의 결과는 내림차순이 됩니다. 이렇게 구현한 MyCompare 클래스를 new TreeSet<String>(new MyCompare()); 처럼 TreeSet<String> 을 생성할 때 생성자를 통해 넘겨주면 정렬 기준이 바뀝니다. 따라서 결과적으로 출력되는 순서는 "ccc", "bbb", "aaa" 로 나오게 됩니다.
'Java' 카테고리의 다른 글
자바의 내부 클래스 (0) | 2021.05.05 |
---|---|
자바의 유용한 클래스 - Class 클래스 (0) | 2021.04.28 |
자바의 메모리 영역과 가상 메서드 (0) | 2021.04.17 |