Java TreeSet 완전 정복: 연산, 메서드, 실전 예제 총정리
Java TreeSet
이 튜토리얼에서는 예제를 통해 Java TreeSet 클래스의 다양한 연산과 메서드를 학습합니다.
Java Collections Framework의 TreeSet 클래스는 트리 자료구조의 기능을 제공합니다.
NavigableSet 인터페이스를 확장합니다.
TreeSet 생성하기
트리 집합을 생성하려면 먼저 java.util.TreeSet 패키지를 임포트해야 합니다.
패키지 임포트 후 Java에서 TreeSet을 생성하는 방법은 다음과 같습니다.
TreeSet<Integer> numbers = new TreeSet<>();
여기서는 인수 없이 TreeSet을 생성했습니다. 이 경우 TreeSet의 요소들은 자연스럽게 정렬됩니다(오름차순).
하지만 Comparator 인터페이스를 사용하여 요소 정렬을 커스터마이징할 수도 있습니다. 이에 대해서는 나중에 학습하겠습니다.
TreeSet의 메서드
TreeSet 클래스는 집합에서 다양한 연산을 수행할 수 있는 여러 메서드를 제공합니다.
TreeSet에 요소 삽입하기
add() - 지정된 요소를 집합에 삽입합니다.
addAll() - 지정된 컬렉션의 모든 요소를 집합에 삽입합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> evenNumbers = new TreeSet<>();
// add() 메서드 사용
evenNumbers.add(2);
evenNumbers.add(4);
evenNumbers.add(6);
System.out.println("TreeSet: " + evenNumbers);
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
// addAll() 메서드 사용
numbers.addAll(evenNumbers);
System.out.println("New TreeSet: " + numbers);
}
}
실행 결과
TreeSet: [2, 4, 6]
New TreeSet: [1, 2, 4, 6]
TreeSet 요소 접근하기
트리 집합의 요소에 접근하려면 iterator() 메서드를 사용할 수 있습니다. 이 메서드를 사용하려면 java.util.Iterator 패키지를 임포트해야 합니다. 예를 들어,
import java.util.TreeSet;
import java.util.Iterator;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// iterator() 메서드 호출
Iterator<Integer> iterate = numbers.iterator();
System.out.print("TreeSet using Iterator: ");
// 요소 접근
while(iterate.hasNext()) {
System.out.print(iterate.next());
System.out.print(", ");
}
}
}
실행 결과
TreeSet: [2, 5, 6]
TreeSet using Iterator: 2, 5, 6,
요소 제거하기
remove() - 지정된 요소를 집합에서 제거합니다.
removeAll() - 집합의 모든 요소를 제거합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// remove() 메서드 사용
boolean value1 = numbers.remove(5);
System.out.println("Is 5 removed? " + value1);
// removeAll() 메서드 사용
boolean value2 = numbers.removeAll(numbers);
System.out.println("Are all elements removed? " + value2);
}
}
실행 결과
TreeSet: [2, 5, 6]
Is 5 removed? true
Are all elements removed? true
탐색을 위한 메서드
TreeSet 클래스가 NavigableSet을 구현하므로, 트리 집합의 요소들을 탐색하는 다양한 메서드를 제공합니다.
1. first()와 last() 메서드
first() - 집합의 첫 번째 요소를 반환합니다.
last() - 집합의 마지막 요소를 반환합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// first() 메서드 사용
int first = numbers.first();
System.out.println("First Number: " + first);
// last() 메서드 사용
int last = numbers.last();
System.out.println("Last Number: " + last);
}
}
실행 결과
TreeSet: [2, 5, 6]
First Number: 2
Last Number: 6
2. ceiling(), floor(), higher(), lower() 메서드
- higher(element) - 지정된
element보다 큰 요소들 중 가장 낮은 요소를 반환합니다.
- lower(element) - 지정된
element보다 작은 요소들 중 가장 큰 요소를 반환합니다.
- ceiling(element) - 지정된 element보다 크거나 같은 요소들 중 가장 낮은 요소를 반환합니다. 전달된 element가 트리 집합에 존재하는 경우, 인수로 전달된
element를 반환합니다.
- floor(element) - 지정된
element보다 작거나 같은 요소들 중 가장 큰 요소를 반환합니다. 전달된 element가 트리 집합에 존재하는 경우, 인수로 전달된 element를 반환합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(4);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// higher() 사용
System.out.println("Using higher: " + numbers.higher(4));
// lower() 사용
System.out.println("Using lower: " + numbers.lower(4));
// ceiling() 사용
System.out.println("Using ceiling: " + numbers.ceiling(4));
// floor() 사용
System.out.println("Using floor: " + numbers.floor(3));
}
}
실행 결과
TreeSet: [2, 4, 5, 6]
Using higher: 5
Using lower: 2
Using ceiling: 4
Using floor: 2
3. pollFirst()와 pollLast() 메서드
pollFirst() - 집합에서 첫 번째 요소를 반환하고 제거합니다.
pollLast() - 집합에서 마지막 요소를 반환하고 제거합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(4);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// pollFirst() 사용
System.out.println("Removed First Element: " + numbers.pollFirst());
// pollLast() 사용
System.out.println("Removed Last Element: " + numbers.pollLast());
System.out.println("New TreeSet: " + numbers);
}
}
실행 결과
TreeSet: [2, 4, 5, 6]
Removed First Element: 2
Removed Last Element: 6
New TreeSet: [4, 5]
4. headSet(), tailSet(), subSet() 메서드
headSet(element, booleanValue)
headSet() 메서드는 지정된 element(인수로 전달) 이전의 트리 집합의 모든 요소를 반환합니다.
booleanValue 매개변수는 선택 사항이며 기본값은 false입니다.
booleanValue로 true를 전달하면 지정된 요소를 포함한 그 이전의 모든 요소를 반환합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(4);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// 기본 boolean 값으로 headSet() 사용
System.out.println("Using headSet without boolean value: " + numbers.headSet(5));
// 지정된 boolean 값으로 headSet() 사용
System.out.println("Using headSet with boolean value: " + numbers.headSet(5, true));
}
}
실행 결과
TreeSet: [2, 4, 5, 6]
Using headSet without boolean value: [2, 4]
Using headSet with boolean value: [2, 4, 5]
tailSet(element, booleanValue)
tailSet() 메서드는 지정된 element(매개변수로 전달) 이후의 트리 집합의 모든 요소를 지정된 element를 포함하여 반환합니다.
booleanValue 매개변수는 선택 사항이며 기본값은 true입니다.
booleanValue로 false를 전달하면 지정된 element를 포함하지 않고 그 이후의 모든 요소를 반환합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(4);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// 기본 boolean 값으로 tailSet() 사용
System.out.println("Using tailSet without boolean value: " + numbers.tailSet(4));
// 지정된 boolean 값으로 tailSet() 사용
System.out.println("Using tailSet with boolean value: " + numbers.tailSet(4, false));
}
}
실행 결과
TreeSet: [2, 4, 5, 6]
Using tailSet without boolean value: [4, 5, 6]
Using tailSet with boolean value: [5, 6]
subSet(e1, bv1, e2, bv2)
subSet() 메서드는 e1과 e2 사이의 모든 요소를 e1을 포함하여 반환합니다.
bv1과 bv2는 선택적 매개변수입니다. bv1의 기본값은 true이고, bv2의 기본값은 false입니다.
bv1로 false를 전달하면 e1을 포함하지 않고 e1과 e2 사이의 모든 요소를 반환합니다.
bv2로 true를 전달하면 e1과 e2 사이의 모든 요소를 e1을 포함하여 반환합니다.
예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(2);
numbers.add(5);
numbers.add(4);
numbers.add(6);
System.out.println("TreeSet: " + numbers);
// 기본 boolean 값으로 subSet() 사용
System.out.println("Using subSet without boolean value: " + numbers.subSet(4, 6));
// 지정된 boolean 값으로 subSet() 사용
System.out.println("Using subSet with boolean value: " + numbers.subSet(4, false, 6, true));
}
}
실행 결과
TreeSet: [2, 4, 5, 6]
Using subSet without boolean value: [4, 5]
Using subSet with boolean value: [5, 6]
집합 연산
TreeSet 클래스의 메서드들은 다양한 집합 연산을 수행하는 데에도 사용할 수 있습니다.
집합의 합집합
두 집합 간의 합집합을 수행하려면 addAll() 메서드를 사용합니다. 예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> evenNumbers = new TreeSet<>();
evenNumbers.add(2);
evenNumbers.add(4);
System.out.println("TreeSet1: " + evenNumbers);
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("TreeSet2: " + numbers);
// 두 집합의 합집합
numbers.addAll(evenNumbers);
System.out.println("Union is: " + numbers);
}
}
실행 결과
TreeSet1: [2, 4]
TreeSet2: [1, 2, 3]
Union is: [1, 2, 3, 4]
집합의 교집합
두 집합 간의 교집합을 수행하려면 retainAll() 메서드를 사용합니다. 예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> evenNumbers = new TreeSet<>();
evenNumbers.add(2);
evenNumbers.add(4);
System.out.println("TreeSet1: " + evenNumbers);
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("TreeSet2: " + numbers);
// 두 집합의 교집합
numbers.retainAll(evenNumbers);
System.out.println("Intersection is: " + numbers);
}
}
실행 결과
TreeSet1: [2, 4]
TreeSet2: [1, 2, 3]
Intersection is: [2]
집합의 차집합
두 집합 간의 차집합을 계산하려면 removeAll() 메서드를 사용할 수 있습니다. 예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> evenNumbers = new TreeSet<>();
evenNumbers.add(2);
evenNumbers.add(4);
System.out.println("TreeSet1: " + evenNumbers);
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
System.out.println("TreeSet2: " + numbers);
// 두 집합의 차집합
numbers.removeAll(evenNumbers);
System.out.println("Difference is: " + numbers);
}
}
실행 결과
TreeSet1: [2, 4]
TreeSet2: [1, 2, 3, 4]
Difference is: [1, 3]
부분집합 확인
한 집합이 다른 집합의 부분집합인지 확인하려면 containsAll() 메서드를 사용합니다. 예를 들어,
import java.util.TreeSet;
class Main {
public static void main(String[] args) {
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
System.out.println("TreeSet1: " + numbers);
TreeSet<Integer> primeNumbers = new TreeSet<>();
primeNumbers.add(2);
primeNumbers.add(3);
System.out.println("TreeSet2: " + primeNumbers);
// primeNumbers가 numbers의 부분집합인지 확인
boolean result = numbers.containsAll(primeNumbers);
System.out.println("Is TreeSet2 subset of TreeSet1? " + result);
}
}
실행 결과
TreeSet1: [1, 2, 3, 4]
TreeSet2: [2, 3]
Is TreeSet2 subset of TreeSet1? true
TreeSet의 기타 메서드
| 메서드 | 설명 |
clone() | TreeSet의 복사본을 생성합니다. |
contains() | 지정된 요소에 대해 TreeSet을 검색하고 불리언 결과를 반환합니다. |
isEmpty() | TreeSet이 비어 있는지 확인합니다. |
size() | TreeSet의 크기를 반환합니다. |
clear() | TreeSet의 모든 요소를 제거합니다. |
더 자세한 내용은 공식 Java 문서 Java TreeSet을 참조하세요.
TreeSet vs HashSet
TreeSet과 HashSet 모두 Set 인터페이스를 구현합니다. 그러나 둘 사이에는 몇 가지 차이점이 존재합니다.
HashSet과 달리 TreeSet의 요소들은 정렬된 순서로 저장됩니다. 왜냐하면 TreeSet이 SortedSet 인터페이스도 구현하기 때문입니다.
TreeSet은 손쉬운 탐색을 위한 메서드들을 제공합니다. 예를 들어 first(), last(), headSet(), tailSet() 등. 이는 TreeSet이 NavigableSet 인터페이스도 구현하기 때문입니다.
HashSet은 TreeSet보다 add, remove, contains, size 같은 기본 연산에서 더 빠릅니다.
TreeSet Comparator
앞의 모든 예제에서 트리 집합 요소들은 자연스럽게 정렬되었습니다. 그러나 요소들의 정렬 순서를 커스터마이징할 수도 있습니다.
이를 위해 트리 집합에서 요소들이 정렬되는 기준이 되는 자체 비교자(comparator) 클래스를 생성해야 합니다. 예를 들어,
import java.util.TreeSet;
import java.util.Comparator;
class Main {
public static void main(String[] args) {
// 커스텀 비교자로 트리 집합 생성
TreeSet<String> animals = new TreeSet<>(new CustomComparator());
animals.add("Dog");
animals.add("Zebra");
animals.add("Cat");
animals.add("Horse");
System.out.println("TreeSet: " + animals);
}
// 비교자 클래스 생성
public static class CustomComparator implements Comparator<String> {
@Override
public int compare(String animal1, String animal2) {
int value = animal1.compareTo(animal2);
// 요소들을 역순으로 정렬
if (value > 0) {
return -1;
} else if (value < 0) {
return 1;
} else {
return 0;
}
}
}
}
실행 결과
TreeSet: [Zebra, Horse, Dog, Cat]
위 예제에서 CustomComparator 클래스를 인수로 전달하여 트리 집합을 생성했습니다.
CustomComparator 클래스는 Comparator 인터페이스를 구현합니다.
그런 다음 compare() 메서드를 오버라이드했습니다. 이제 이 메서드는 요소들을 역순으로 정렬합니다.
더 자세한 내용은 공식 Java 문서 Java Comparator를 참조하세요.