티스토리 뷰
용어 정리
- 이진 트리 모든 노드가 최대 2개의 하위 노드를 갖음
- 이진 탐색 트리 부모보다 작은 값은 왼쪽. 큰 값은 오른쪽에 저장(이진 트리 중 한 종류)
- TreeSet 이진 탐색 트리(binary search tree)로 구현
TreeSet - 범위 탐색, 정렬
- 이진 탐색 트리(binary search tree)로 구현. 범위 탐색과 정렬에 유리
- 이진 트리는 모든 노드가 최대 2개의 하위 노드를 갖음
- 각 요소(node)가 나무(tree)형태로 연결(LinkedList의 변형)
이진 트리는 모든 노드가 최대 2개의 하위 노드를 갖음
각 요소(node)가 나무(tree)형태로 연결(LinkedList의 변형)

이진 탐색 트리(binary search tree)
- 부모보다 작은 값은 왼쪽. 큰 값은 오른쪽에 저장(왼작오큰)
- 데이터가 많아질 수록 추가, 삭제에 시간이 더 걸림(비교 횟수 증가)
부모보다 작은 값은 왼쪽, 큰값은 오른쪽에 저장

데이터가 많아질 수록 추가, 삭제에 시간이 더 걸림(비교 횟수 증가)
데이터가 많아지면, 트리가 커진다. 트리가 커지면 요소를 하나 저장할 때 마다 부모보다 큰지 작은지 비교해야 한다. 이진 탐색 트리는 부모보다 작은 값을 왼쪽, 큰 값을 오른쪽에 저장해야 하기 때문이다. 즉, 트리가 커질수록 비교횟수가 늘어나기 때문에 저장하는데 시간이 더 걸린다. 삭제할 때에도 마찬가지이다.
TreeSet의 데이터 저장과정 boolean add(Object obj)
- TreeSet에 7, 4, 9, 1, 5의 순서로 데이터를 저장하면, 아래의 과정을 거친다.
- 루트부터 트리를 따라 내려가며 값을 비교. 작으면 왼쪽, 크면 오른쪽에 저장
boolean add(Object obj)
TreeSet에 데이터를 저장할 땐,add메서드를 사용한다. Object는 저장할 객체이다. add메서드를 호출하면 1. equals()와 2. hashCode()를 호출한다.
| 참고 | HashSet은 equals(), hashCode()로 비교하고, TreeSet은 compare()를 호출해서 비교한다.
TreeSet에 7, 4, 9, 1, 5의 순서로 데이터를 저장하면, 아래의 과정을 거친다.

비교는 항상 루트부터 한다.
TreeSet의 주요 생성자와 메서드
생성자

메서드

예제11-13
결과
[5, 8, 9, 26, 29, 30]
난수 데이터를 TreeSet(이진 탐색 트리)에 저장했기 때문에, 요소들이 정렬되어 출력되었다.
[테스트1] HashSet에 저장할 경우 출력 결과 테스트
public class Ex11_13 {
public static void main(String[] args) {
// Set set = new TreeSet(); // 범위 검색, 정렬, 정렬 필요없음
Set set = new HashSet(); // 정렬 필요
결과
[32, 33, 2, 39, 28, 31]
예제11-10(Ex_10)과 비교해보면, HashSet을 LinkedList로 옮긴 다음에 sort()를 호출하여 정렬했다. 정렬하려면 순서를 유지하는 리스트(List)여야 하기 때문에 이 과정을 거쳤다. 반면, TreeSet으로 할 경우, 정렬을 하지 않았지만 정렬이 되어 출력되는 것을 확인 할 수 있다. 이것이 TreeSet의 장점이다. 정렬을 할 필요가 없다.
[테스트2] Test클래스 생성하여 비교기준 필요성여부 테스트
- Test클래스를 새로 생성(내용x)
- Test객체를 생성해서 TreeSet에 저장
- set 출력
코드
import java.util.*;
public class Ex11_13 {
public static void main(String[] args) {
Set set = new TreeSet(); // 범위 검색, 정렬, 정렬 필요없음
// Set set = new HashSet(); // 정렬 필요(Ex11_10과 비교)
for (int i = 0; set.size() < 6 ; i++) {
int num = (int)(Math.random()*45) + 1;
set.add(new Test()); // set.add(new Integer(num));
}
System.out.println(set);
}
}
class Test {}
결과
Exception in thread "main" java.lang.ClassCastException: class ch11.Test cannot be cast to class java.lang.Comparable (ch11.Test is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
at java.base/java.util.TreeMap.put(TreeMap.java:536)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at ch11.Ex11_13.main(Ex11_13.java:12)
형변환 예외가 발생한다.
Test클래스의 객체를 반복문을 통해 저장을 하는데, set의 add메서드는 '비교기준'으로 비교한 후 저장을 한다. 하지만, '비교기준'이 없기 때문에 에러가 발생했다.
class Test {} // 비교 기준이 없음.
[테스트3] Test클래스 생성하여 비교기준 필요성여부 테스트2
- 비교 기준을 만들어주기 위해 TestComp클래스를 새로 생성(내용x)
- 비교 기준이 되려면 TestComp클래스는 Comparator를 구현해야 된다.
- compare메서드를 오버라이딩한다.
- TreeSet() 기본 생성자를 호출하는 대신, 매개변수 있는 생성자 TreeSet(Comparator comp)를 사용하기 위해 new TestComp()로 TestComp클래스의 객체를 매개변수로 대입한다.
코드
import java.util.*;
public class Ex11_13 {
public static void main(String[] args) {
Set set = new TreeSet(new TestComp()); // 범위 검색, 정렬, 정렬 필요없음
// Set set = new HashSet(); // 정렬 필요(Ex11_10과 비교)
// for (int i = 0; set.size() < 6 ; i++) {
// int num = (int)(Math.random()*45) + 1;
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
// }
System.out.println(set);
}
}
class Test {} // 비교 기준이 없음.
class TestComp implements Comparator {
@Override
public int compare(Object o1, Object o2) {
return 1;
}
}
결과
[ch11.Test@56cbfb61, ch11.Test@1134affc, ch11.Test@d041cf, ch11.Test@129a8472]
[테스트4] Test클래스가 Comparable를 구현할 경우 테스트
코드
import java.util.*;
public class Ex11_13 {
public static void main(String[] args) {
Set set = new TreeSet(); // 범위 검색, 정렬, 정렬 필요없음
// Set set = new HashSet(); // 정렬 필요(Ex11_10과 비교)
// for (int i = 0; set.size() < 6 ; i++) {
// int num = (int)(Math.random()*45) + 1;
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
set.add(new Test()); // set.add(new Integer(num));
// }
System.out.println(set);
}
}
class Test implements Comparable{
@Override
public int compareTo(Object o) {
return -1;
}
} // 비교 기준이 없음.
class TestComp implements Comparator {
@Override
public int compare(Object o1, Object o2) {
return 1;
}
}
결과
[ch11.Test@129a8472, ch11.Test@1b0375b3, ch11.Test@2f7c7260, ch11.Test@2d209079]
// Set set = new TreeSet(new TestComp()); // 범위 검색, 정렬, 정렬 필요없음
Set set = new TreeSet(); // 범위 검색, 정렬, 정렬 필요없음
결론 : '비교 기준'은 반드시 필요하다.
이때 TreeSet의 기본생성자를 사용해도 에러가 발생하지 않는다. 비교 기준이 없으면 TreeSet이 제대로 작동하지 않는다.
둘 중에 하나를 해야 한다. 저장하는 객체가 Comparable을 갖고 있든지, TreeSet이 어떤 정렬 기준을 갖도록 해야 한다. 이렇게 둘 중 하나의 요건을 충족해야 한다. 어디에서 제공하든지 '비교기준'이 필요하다.
결론2 : Integer클래스는 Comparable을 구현하고 있다.
따라서, set.add(new Integer(num));으로 set에 숫자를 추가했을 때, TreeSet의 기본 생성자를 호출하여도 에러가 발생하지 않았다.
예제11-14
subSet() 사용 방법을 연습할 수 있다.
String from = "b";
String to = "d";
set.add("abc"); set.add("alien"); set.add("bat");
set.add("car"); set.add("Car"); set.add("disc");
set.add("dance"); set.add("dZZZZ"); set.add("dzzzz");
set.add("elephant"); set.add("elevator"); set.add("fan");
set.add("flower");
System.out.println("result1 : " + set.subSet(from, to));
System.out.println("result2 : " + set.subSet(from, to + "zzz"));
set.subSet(from, to);
→ set.subSet("b", "d"); // "d" 포함 안 됨
set.subSet(from, to + "zzz");
→ set.subSet("b", "d" + "zzz");
→ set.subSet("b", "dzzz"); // "d" 포함 문자열 중 맨끝 문자열만 비포함 즉 나머지는 포함
예제11-15
예제11-15
- headSet()과 tailSet()을 사용하여 score배열에 저장된 객체들이 50보다 큰지 작은지 확인하는 예제

[테스트] 참조변수를 TreeSet에서 Set으로 형변환 테스트

Cannot resolve method 'headSet' in 'Set'
Cannot resolve method 'tailSet' in 'Set'
headSet()과 tailSet()은 TreeSet에서만 존재하는 메서드이기 때문에, 참조변수 타입을 TreeSet에서 Set으로 변경하면 안 된다.
TreeSet의 범위 검색 - subSet(), headSet(), tailSet()
subSet(), headSet(), tailSet()


[알아두면 좋아요] 트리 순회(tree traversal)
- 트리 순회 : 이진 트리의 모든 노드를 한번씩 읽는 것
- 전위, 중위, 후위 순회법이 있으며, 중위 순회하면 오름차순으로 정렬된다.
트리 순회의 종류

'Java의 정석_기초편' 카테고리의 다른 글
| Collections (0) | 2022.11.05 |
|---|---|
| HashMap (0) | 2022.11.05 |
| HashSet (0) | 2022.11.03 |
| Comparator와 Comparable (0) | 2022.11.03 |
| Arrays (0) | 2022.11.03 |
