자바에서 Set은 엘리먼트들의 중복을 제거하기위해 사용된다. 대표적인 구현체로 HashSet이 많이 사용된다. HashSet의 경우 담고 있는 엘리먼트들의 해시 값에 따라 무작위 순서로 순회하게 된다.
반면 SortedSet의 경우 중복을 제거하면서 엘리먼트들의 저장되는 순서를 관리할 수 있다. SortedSet은 이 순서에 따라 엘리먼트들을 순회하게 된다.
SortedSet
이름에서 알 수 있듯이 SortedSet은 엘리먼트들을 정렬된 순서로 저장한다. 저장되는 엘리먼트가 Comparable 인터페이스를 구현하고 있다면 compare() 메소드의 로직을 이용하게 되며, SortedSet의 구현체를 생성할 때 Comparator 클래스를 넘겨 엘리먼트들의 대소 비교에 사용할 수 있다.
SortedSet은 Set 인터페이스를 상속하고 있으므로 같은 값을 갖는 엘리먼트들이 추가되었을 때 중복을 제거하여 저장할 수 있다.
SortedSet 구현체
SortedSet은 인터페이스이기 때문에 이를 구현한 클래스를 사용해야한다. SortedSet의 대표적인 구현체는 java.util.TreeSet이다. 이 포스트의 예제에서도 TreeSet을 이용하겠다.
SortedSet<String> sortedSet = new TreeSet<>();
TreeSet 객체를 생성하면서 Comparator를 인자로 줄 경우 Comparator의 비교 로직에 따라 엘리먼트들이 정렬된 순서로 저장된다.
Comparator comparator = new MyComparatorImpl();
SortedSet sortedSet = new SortedSet(comparator);
SortedSet은 엘리먼트를 저장할 때 오름차순으로 저장한다. Comparable 혹은 Comparator의 compare()를 기준으로 작은 값부터 시작해 큰 값으로 저장된다.
SortedSet 주요 메소드
Method | 설명 |
---|---|
headSet(E toElement) | toElement(미포함)보다 작은 객체들로 구성된 SortedSet 리턴 |
tailSet(E fromElemet) | fromElemet(포함)보다 큰 객체들로 구성된 SortedSet 리턴 |
subSet(E fromElement, E toElement) | fromElement(포함)보다 크고 toElement(미포함)보다 작은 객체들로 구성된 SortedSet 리턴 |
first() | 가장 작은 엘리먼트 리턴 |
last() | 가장 큰 엘리먼트 리턴 |
그림으로 그려보면 다음과 같다.
각 메소드들의 인자로 넘겨준 엘리먼트가 리턴되는 SortedSet에 포함되는지 여부를 유의해서 사용하면된다.
Set 인터페이스의 구현체도 Java에서 가장 많이 사용되는 자료구조다. HashSet을 가장 많이 사용하겠지만 로직에 따라 TreeSet 같은 클래스도 많이 사용한다. Set을 구현한 각 클래스들의 특징을 잘 알아두면 필요한 곳에 필요한 클래스를 잘 사용할 수 있을 것이다.
SortedSet 예제
TreeSet을 이용해 SortedSet이 어떻게 동작하는지 확인해보자.
엘리먼트 순회
가장 간단한 예제로 SortedSet에 엘리먼트를 저장한 후, 모든 엘리먼트를 순회하면서 값을 찍어보는 코드를 작성해보자.
SortedSet<String> sortedSet = new TreeSet<>();
sortedSet.add("Elem5");
sortedSet.add("Elem3");
sortedSet.add("Elem1");
sortedSet.add("Elem3");
sortedSet.add("Elem1");
for (String elem : sortedSet) {
System.out.println(elem);
}
Elem1과 Elem3라는 값이 중복되어 추가되었고 정렬되지 않은 순서로 SortedSet에 추가된다. 모든 엘리먼트가 저장된 후 순회하면서 값을 찍어보면 다음과 같이 출력된다.
Elem1
Elem3
Elem5
중복제거도 되면서 엘리먼트의 순서에 따라 정렬되어 순회하는 것을 확인할 수 있다.
부분접근 - headSet, tailSet, subSet
정렬된 순서에서 headSet, tailSet, subSet 메소드를 이용해 SortedSet의 부분을 뽑아내는 예제를 작성해보자.
Sorted<String> sortedSet = new TreeSet<>();
sortedSet.add("Elem5");
sortedSet.add("Elem2");
sortedSet.add("Elem4");
sortedSet.add("Elem3");
sortedSet.add("Elem1");
SortedSet<String> headSet = sortedSet.headSet("Elem3");
System.out.println("Head Set");
for (String elem : headSet) {
System.out.println(elem);
}
SortedSet<String> tailSet = sortedSet.tailSet("Elem4");
System.out.println("Tail Set");
for (String elem : tailSet) {
System.out.println(elem);
}
SortedSet<String> subSet = sortedSet.subSet("Elem2", "Elem5");
System.out.println("Sub Set");
for (String elem : subSet) {
System.out.println(elem);
}
SortedSet에 5개의 엘리먼트를 저장한 후, headSet, tailSet, subSet 메소드를 이용해 SortedSet의 부분을 뽑아냈다. 위에서 본 그림대로 결과가 출력되는 것을 볼 수 있다.
Head Set
Elem1
Elem2
Tail Set
Elem4
Elem5
Sub Set
Elem2
Elem3
Elem4
각 메소드의 인자로 넘겨진 값과 동일한 엘리먼트가 결과에 포함되는지 포함되지 않는지를 유의해서 봐야한다.
가장자리 접근 - first, last
정렬된 엘리먼트 중에 가장 큰 값과 작은 값에 접근하는 메소드도 제공된다.
SortedSet<String> sortedSet = new TreeSet<>();
sortedSet.add("Elem5");
sortedSet.add("Elem2");
sortedSet.add("Elem4");
sortedSet.add("Elem3");
sortedSet.add("Elem1");
System.out.println(sortedSet.first());
System.out.println(sortedSet.last());
이를 실행하면
Elem1
Elem5
엘리먼트가 하나도 없는 경우 NoSuchElementException이 발생한다.
댓글