본문 바로가기
Old Posts/Java

[Java] NavigableSet 사용법 및 예제

by A6K 2023. 5. 25.

Java의 NavigableSet 인터페이스는 SortedSet을 상속하는 인터페이스다. Set 인터페이스를 상속한 SortedSet 인터페이스를 상속하고 있기 때문에 NavigableSet 인터페이스를 구현하기 위해서는 Set, SortedSet의 메소드들도 구현해야한다.

NavigableSet

이름에서 알 수 있듯이 NavigableSet은 저장되는 엘리먼트들에 Navigation 기능을 더한다. NavigableSet의 JavaDocs를 보면 다음과 같은 설명이 있다.

A SortedSet extended with navigation methods reporting closest matches for given search targets.

NavigableSet은 SortedSet을 상속하기 때문에 엘리먼트들이 정렬된 상태로 저장된다. 여기에 더해 찾고자 하는 엘리먼트 값과 가장 가까운 엔트리를 찾아주는 lower(), floor(), ceiling(), higher() 같은 Navigation 메소드가 추가되었다.

NavigableSet 구현체

NavigableSet은 인터페이스다. 따라서 실제 로직에 사용하기 위해서는 NavigableSet에 정의된 메소드들을 구현한 클래스를 사용해야한다. 가장 널리 사용되는 구현체는 TreeSet이다.

Set<String> set = new TreeSet<>();
SortedSet<String> sortedSet = new TreeSet<>();
NavigableSet<String> navigableSet = new TreeSet<>();

TreeSet에 담는 클래스가 Comparable 인터페이스를 구현하고 있다면 compare() 메소드의 로직에 따라 저장되는 순서가 결정된다. 혹은 엘리먼트들을 비교할 때 사용할 Comparator 클래스를 TreeSet 생성시 넘겨주면 마찬가지로 엘리먼트의 저장 순서에 영향을 줄 수 있다.

NavigableSet 메소드

NavigableSet에서 추가로 정의하는 주요 메소드는 다음과 같다.

Method 설명
lower(E e) e 보다 작은 엘리먼트들 중에서 가장 큰 엘리먼트를 리턴 (e 미만)
floor(E e) e 보다 작은 엘리먼트들 중에서 가장 큰 엘리먼트를 리턴 (e 이하)
ceiling(E e) e 보다 큰 엘리먼트들 중에서 가장 작은 엘리먼트 리턴 (e 이상)
higher(E e) e 보다 큰 엘리먼트들 중에서 가장 작은 엘리먼트 리턴 (e 초과)
pollFirst() 가장 작은 엘리먼트를 가져오고 Set에서 제거
pollLast() 가장 큰 엘리먼트를 가져오고 Set에서 제거
iterator() 엘리먼트를 순회할 수 있는 Iterator 리턴
descendingSet() 내림차순으로 저장하는 NavigableSet 리턴
descendingIterator() 내림차순으로 엘리먼트를 순회할 수 있는 Iterator 리턴
subSet(E fromElement, E toElement) fromElement(포함)보다 크고 toElement(비포함)보다 작은 NavigableSet 리턴
headSet(E toElement) toElement(비포함)보다 작은 엘리먼트로 구성된 NavigableSet 리턴
tailSet(E fromElement) fromElement(포함)보다 큰 엘리먼트로 구성된 NavigableSet 리턴

NavigableSet 예제

예제 코드를 통해 NavigableSet이 정의하는 메소드의 특성을 확인해보자.

가장 근접한 원소 - ceiling, floor, higher, lower

어떤 값을 기준으로 가장 근접하게 크거나 작은 원소를 리턴하는 Navigation 메소드들이다. 다음과 같은 코드를 생각해보자.

NavigableSet<String> navigableSet = new TreeSet<>();

navigableSet.add("김민호");
navigableSet.add("한승엽");
navigableSet.add("이민성");
navigableSet.add("박승민");
navigableSet.add("최하나");

System.out.println(navigableSet.ceiling("이"));
System.out.println(navigableSet.floor("이"));
System.out.println(navigableSet.ceiling("이민성"));
System.out.println(navigableSet.floor("박승민"));
System.out.println(navigableSet.lower("이민성"));
System.out.println(navigableSet.higher("박승민"));

5명의 이름을 NavigableSet에 추가했다. 추가된 이름은 "김민호"-"박승민"-"이민성"-"최하나"-"한승엽" 순으로 정렬되어 저장될 것이다. 이 상태에서 ceiling, floor, lower, higher 메소드를 실행해보자. 위 코드를 실행하면 다음 결과를 얻는다.

이민성
박승민
이민성
박승민
박승민
이민성

우선 ceiling() 메소드는 인자로 받은 값보다 큰 값들 중에서 가장 작은 값을 리턴한다. 인자로 받은 "이"보다 큰 값인 "이민성", "최하나", "한승엽" 중에 가장 작은 값인 "이민성"을 리턴한다.

floor() 메소드 역시 유사하다. 인자로 받은 값보다 작은 값들 중에서 가장 큰 값을 리턴한다. 인자로 받은 "이"보다 작은 값인 "김민호", "박승민" 중 가장 큰 값인 "박승민"을 리턴한다.

lower()와 higher()는 floor()와 ceiling()의 동작과 유사하다. 다만 인자로 받은 값과 일치하는 엘리먼트가 존재했을 때의 동작이 다르다. floor()와 ceiling()이 인자로 받은 값과 일치하는 엘리먼트를 포함하는 반면 lower()와 higher()는 인자로 받은 값과 일치하는 엘리먼트를 포함하지 않는다. 즉 정수에 대한 이상/이하와 초과/미만의 관계라고 생각하면 된다.

엘리먼트 순회

엘리먼트를 순회하는 코드를 보자.

NavigableSet<String> navigableSet = new TreeSet<>();

navigableSet.add("김민호");
navigableSet.add("한승엽");
navigableSet.add("이민성");
navigableSet.add("박승민");
navigableSet.add("최하나");

System.out.println("NavigableSet 순서");
for (String elem : navigableSet) {
  System.out.println(elem);
}

System.out.println("NavigableSet.descendingSet() 순서");
for (String elem : navigableSet.descendingSet()) {
  System.out.println(elem);
}

이 코드를 실행하면 다음 결과를 얻게 된다.

NavigableSet 순서
김민호
박승민
이민성
최하나
한승엽
NavigableSet.descendingSet() 순서
한승엽
최하나
이민성
박승민
김민호

descendingSet()을 이용해 역순정렬된 NavigableSet을 얻고, 엘리먼트들을 순회해봤다.

댓글