정렬된 맵 - TreeMap 마스터하기
Java API Reference
import java.util.*;
public class TreeMapBasic {
public static void main(String[] args) {
System.out.println("=== TreeMap 특징 ===\n");
System.out.println("1. Key-Value 쌍");
System.out.println(" - Key 중복 불가\n");
System.out.println("2. 자동 정렬");
System.out.println(" - Key 기준 오름차순");
System.out.println(" - Comparable/Comparator\n");
System.out.println("3. 성능");
System.out.println(" - put: O(log n)");
System.out.println(" - get: O(log n)");
System.out.println(" - remove: O(log n)\n");
System.out.println("4. null 불가");
System.out.println(" - Key에 null 불가");
System.out.println(" - Value는 null 가능");
}
}public class AutoSort {
public static void main(String[] args) {
// TreeMap (자동 정렬)
Map<Integer, String> treeMap = new TreeMap<>();
treeMap.put(5, "Five");
treeMap.put(2, "Two");
treeMap.put(8, "Eight");
treeMap.put(1, "One");
System.out.println("TreeMap: " + treeMap);
// {1=One, 2=Two, 5=Five, 8=Eight}
// HashMap (순서 없음)
Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(5, "Five");
hashMap.put(2, "Two");
hashMap.put(8, "Eight");
hashMap.put(1, "One");
System.out.println("HashMap: " + hashMap);
// 무작위
}
}public class TreeMapCreation {
public static void main(String[] args) {
// 기본 생성 (자연 순서)
TreeMap<Integer, String> map1 = new TreeMap<>();
System.out.println("기본: " + map1);
// 다른 Map으로
TreeMap<Integer, String> map2 = new TreeMap<>();
map2.put(3, "Three");
map2.put(1, "One");
map2.put(2, "Two");
TreeMap<Integer, String> map3 = new TreeMap<>(map2);
System.out.println("복사: " + map3);
// {1=One, 2=Two, 3=Three}
// Comparator로 (역순)
TreeMap<Integer, String> map4 = new TreeMap<>(
Collections.reverseOrder()
);
map4.put(1, "One");
map4.put(2, "Two");
map4.put(3, "Three");
System.out.println("역순: " + map4);
// {3=Three, 2=Two, 1=One}
// SortedMap 타입 (권장)
SortedMap<Integer, String> map5 = new TreeMap<>();
System.out.println("SortedMap: " + map5);
}
}public class CustomSort {
static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
public static void main(String[] args) {
// 나이순 정렬
TreeMap<Person, String> map1 = new TreeMap<>(
Comparator.comparingInt(p -> p.age)
);
map1.put(new Person("Alice", 25), "Engineer");
map1.put(new Person("Bob", 20), "Student");
map1.put(new Person("Charlie", 30), "Manager");
System.out.println("나이순:");
map1.forEach((person, job) -> {
System.out.println(person + ": " + job);
});
// 이름순 정렬
TreeMap<Person, String> map2 = new TreeMap<>(
Comparator.comparing(p -> p.name)
);
map2.putAll(map1);
System.out.println("\n이름순:");
map2.forEach((person, job) -> {
System.out.println(person + ": " + job);
});
}
}public class TreeMapPut {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
// 추가 (자동 정렬)
map.put(5, "Five");
map.put(2, "Two");
map.put(8, "Eight");
System.out.println("추가 후: " + map);
// {2=Two, 5=Five, 8=Eight}
// 중복 Key (덮어쓰기)
map.put(5, "FIVE");
System.out.println("수정 후: " + map);
// {2=Two, 5=FIVE, 8=Eight}
// putIfAbsent
map.putIfAbsent(1, "One");
map.putIfAbsent(5, "Five"); // 무시
System.out.println("putIfAbsent: " + map);
// null Key (에러!)
try {
map.put(null, "Null");
} catch (NullPointerException e) {
System.out.println("\nnull Key 불가!");
}
}
}public class TreeMapRemove {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.put(4, "Four");
map.put(5, "Five");
System.out.println("원본: " + map);
// Key로 삭제
map.remove(3);
System.out.println("3 삭제: " + map);
// 첫 Entry 삭제
Map.Entry<Integer, String> first = map.pollFirstEntry();
System.out.println("pollFirst: " + first);
System.out.println("After: " + map);
// 마지막 Entry 삭제
Map.Entry<Integer, String> last = map.pollLastEntry();
System.out.println("pollLast: " + last);
System.out.println("After: " + map);
}
}public class TreeMapFirstLast {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(5, "Five");
map.put(2, "Two");
map.put(8, "Eight");
map.put(1, "One");
System.out.println("Map: " + map);
// {1=One, 2=Two, 5=Five, 8=Eight}
// 첫/마지막 Key
Integer firstKey = map.firstKey();
Integer lastKey = map.lastKey();
System.out.println("First key: " + firstKey); // 1
System.out.println("Last key: " + lastKey); // 8
// 첫/마지막 Entry
Map.Entry<Integer, String> firstEntry = map.firstEntry();
Map.Entry<Integer, String> lastEntry = map.lastEntry();
System.out.println("First entry: " + firstEntry);
System.out.println("Last entry: " + lastEntry);
}
}public class TreeMapNearest {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(1, "One");
map.put(3, "Three");
map.put(5, "Five");
map.put(7, "Seven");
map.put(9, "Nine");
System.out.println("Map: " + map);
// lowerKey: 작은 Key 중 가장 큰 것
System.out.println("\nlowerKey(5): " + map.lowerKey(5)); // 3
System.out.println("lowerKey(6): " + map.lowerKey(6)); // 5
// floorKey: 작거나 같은 Key 중 가장 큰 것
System.out.println("\nfloorKey(5): " + map.floorKey(5)); // 5
System.out.println("floorKey(6): " + map.floorKey(6)); // 5
// ceilingKey: 크거나 같은 Key 중 가장 작은 것
System.out.println("\nceilingKey(5): " + map.ceilingKey(5)); // 5
System.out.println("ceilingKey(6): " + map.ceilingKey(6)); // 7
// higherKey: 큰 Key 중 가장 작은 것
System.out.println("\nhigherKey(5): " + map.higherKey(5)); // 7
System.out.println("higherKey(6): " + map.higherKey(6)); // 7
}
}public class TreeMapSubMap {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
for (int i = 1; i <= 10; i++) {
map.put(i, "Value" + i);
}
System.out.println("전체: " + map);
// subMap (from 포함, to 미포함)
SortedMap<Integer, String> sub1 = map.subMap(3, 8);
System.out.println("subMap(3, 8): " + sub1);
// {3=Value3, 4=Value4, ..., 7=Value7}
// subMap (포함 여부 지정)
NavigableMap<Integer, String> sub2 = map.subMap(3, true, 8, true);
System.out.println("subMap(3-8 포함): " + sub2);
// {3=Value3, 4=Value4, ..., 8=Value8}
// headMap (처음부터 to 미포함)
SortedMap<Integer, String> head = map.headMap(5);
System.out.println("headMap(5): " + head);
// {1=Value1, 2=Value2, 3=Value3, 4=Value4}
// tailMap (from 포함부터 끝까지)
SortedMap<Integer, String> tail = map.tailMap(7);
System.out.println("tailMap(7): " + tail);
// {7=Value7, 8=Value8, 9=Value9, 10=Value10}
}
}public class TreeMapRangeOps {
public static void main(String[] args) {
TreeMap<Integer, Integer> scores = new TreeMap<>();
scores.put(85, 1);
scores.put(92, 2);
scores.put(78, 3);
scores.put(95, 4);
scores.put(88, 5);
System.out.println("전체: " + scores);
// 80~90점 사이
NavigableMap<Integer, Integer> range = scores.subMap(80, true, 90, true);
System.out.println("80~90점: " + range);
// 90점 이상
NavigableMap<Integer, Integer> high = scores.tailMap(90, true);
System.out.println("90점 이상: " + high);
// 합계
int sum = range.values().stream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("80~90점 합계: " + sum);
}
}public class TreeMapAscending {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(5, "Five");
map.put(2, "Two");
map.put(8, "Eight");
map.put(1, "One");
// EntrySet (오름차순)
System.out.println("=== 오름차순 ===");
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 1, 2, 5, 8
// KeySet
System.out.println("\n=== KeySet ===");
for (Integer key : map.keySet()) {
System.out.println(key);
}
}
}public class TreeMapDescending {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(5, "Five");
map.put(2, "Two");
map.put(8, "Eight");
map.put(1, "One");
// descendingMap (내림차순 뷰)
System.out.println("=== 내림차순 Map ===");
NavigableMap<Integer, String> desc = map.descendingMap();
System.out.println(desc);
// {8=Eight, 5=Five, 2=Two, 1=One}
// descendingKeySet
System.out.println("\n=== descendingKeySet ===");
for (Integer key : map.descendingKeySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}public class TreeMapStructure {
public static void main(String[] args) {
System.out.println("=== TreeMap 구조 ===\n");
System.out.println("내부: 레드-블랙 트리");
System.out.println("특징: 자가 균형 이진 탐색 트리");
System.out.println("높이: O(log n) 유지\n");
System.out.println("=== 정렬 보장 ===");
System.out.println("중위 순회 = 정렬 순서");
System.out.println("Comparable 또는 Comparator\n");
System.out.println("=== Entry 저장 ===");
System.out.println("Key: 정렬 기준");
System.out.println("Value: 함께 저장");
}
}public class TreeMapComplexity {
public static void main(String[] args) {
System.out.println("=== 시간 복잡도 ===\n");
System.out.println("put: O(log n)");
System.out.println(" - 트리 탐색 + 삽입\n");
System.out.println("get: O(log n)");
System.out.println(" - 이진 탐색\n");
System.out.println("remove: O(log n)");
System.out.println(" - 탐색 + 삭제 + 균형\n");
System.out.println("firstKey: O(log n)");
System.out.println("lastKey: O(log n)\n");
System.out.println("HashMap보다 느림");
System.out.println("하지만 정렬 보장!");
}
}public class PerformanceComparison {
public static void main(String[] args) {
int n = 100000;
// HashMap
Map<Integer, String> hashMap = new HashMap<>();
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
hashMap.put(i, "Value" + i);
}
long time1 = System.currentTimeMillis() - start;
// TreeMap
Map<Integer, String> treeMap = new TreeMap<>();
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
treeMap.put(i, "Value" + i);
}
long time2 = System.currentTimeMillis() - start;
System.out.println("HashMap: " + time1 + "ms");
System.out.println("TreeMap: " + time2 + "ms");
System.out.println("\nTreeMap이 느림");
System.out.println("하지만 정렬됨!");
}
}public class Leaderboard {
private TreeMap<Integer, String> scores;
public Leaderboard() {
this.scores = new TreeMap<>(Collections.reverseOrder());
}
public void addScore(String player, int score) {
scores.put(score, player);
}
public void showTop5() {
System.out.println("=== Top 5 ===");
int rank = 1;
for (Map.Entry<Integer, String> entry : scores.entrySet()) {
System.out.println(rank + ". " + entry.getValue() +
" - " + entry.getKey() + "점");
if (++rank > 5) break;
}
}
public static void main(String[] args) {
Leaderboard board = new Leaderboard();
board.addScore("Alice", 95);
board.addScore("Bob", 87);
board.addScore("Charlie", 92);
board.addScore("David", 88);
board.addScore("Eve", 91);
board.addScore("Frank", 85);
board.showTop5();
}
}public class RangeQuery {
private TreeMap<Integer, String> data;
public RangeQuery() {
this.data = new TreeMap<>();
}
public void add(int key, String value) {
data.put(key, value);
}
public List<String> getRange(int from, int to) {
return new ArrayList<>(
data.subMap(from, true, to, true).values()
);
}
public String getNearest(int target) {
// 가장 가까운 Key
Integer floor = data.floorKey(target);
Integer ceiling = data.ceilingKey(target);
if (floor == null) return data.get(ceiling);
if (ceiling == null) return data.get(floor);
int key = (target - floor) <= (ceiling - target) ? floor : ceiling;
return data.get(key);
}
public static void main(String[] args) {
RangeQuery query = new RangeQuery();
query.add(10, "Ten");
query.add(20, "Twenty");
query.add(30, "Thirty");
query.add(40, "Forty");
query.add(50, "Fifty");
System.out.println("20~40: " + query.getRange(20, 40));
System.out.println("35에 가까운 값: " + query.getNearest(35));
}
}public class EventTimeline {
private TreeMap<Long, String> events;
public EventTimeline() {
this.events = new TreeMap<>();
}
public void addEvent(long timestamp, String event) {
events.put(timestamp, event);
}
public void showTimeline() {
System.out.println("=== 타임라인 ===");
events.forEach((time, event) -> {
Date date = new Date(time);
System.out.println(date + ": " + event);
});
}
public List<String> getEventsBetween(long start, long end) {
return new ArrayList<>(
events.subMap(start, true, end, true).values()
);
}
public static void main(String[] args) throws InterruptedException {
EventTimeline timeline = new EventTimeline();
long now = System.currentTimeMillis();
timeline.addEvent(now, "Event 1");
Thread.sleep(100);
timeline.addEvent(now + 100, "Event 2");
Thread.sleep(100);
timeline.addEvent(now + 200, "Event 3");
timeline.showTimeline();
}
}// 단어 빈도를 빈도수 기준 정렬
public class Problem1 {
public static Map<String, Integer> sortByFrequency(Map<String, Integer> freq) {
// 빈도수 기준 정렬 (높은 순)
return null;
}
public static void main(String[] args) {
Map<String, Integer> freq = new HashMap<>();
freq.put("apple", 3);
freq.put("banana", 5);
freq.put("cherry", 2);
Map<String, Integer> sorted = sortByFrequency(freq);
System.out.println("빈도순: " + sorted);
// {banana=5, apple=3, cherry=2}
}
}정답:
정답 보기
public class Problem1 {
public static Map<String, Integer> sortByFrequency(Map<String, Integer> freq) {
// Value 기준 역순 정렬
TreeMap<Integer, String> sorted = new TreeMap<>(
Collections.reverseOrder()
);
// Key-Value 뒤집어서 저장 (Value가 Key가 됨)
for (Map.Entry<String, Integer> entry : freq.entrySet()) {
sorted.put(entry.getValue(), entry.getKey());
}
// 다시 원래 형태로
Map<String, Integer> result = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : sorted.entrySet()) {
result.put(entry.getValue(), entry.getKey());
}
return result;
}
// Stream 버전
public static Map<String, Integer> sortByFrequency2(Map<String, Integer> freq) {
return freq.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
}// 범위 내 값들의 합계
public class Problem2 {
public static int rangeSum(TreeMap<Integer, Integer> map, int from, int to) {
// 코드 작성
return 0;
}
public static void main(String[] args) {
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(1, 100);
map.put(3, 300);
map.put(5, 500);
map.put(7, 700);
map.put(9, 900);
int sum = rangeSum(map, 3, 7);
System.out.println("3~7 합계: " + sum);
// 300 + 500 + 700 = 1500
}
}정답:
정답 보기
public class Problem2 {
public static int rangeSum(TreeMap<Integer, Integer> map, int from, int to) {
NavigableMap<Integer, Integer> range = map.subMap(from, true, to, true);
return range.values().stream()
.mapToInt(Integer::intValue)
.sum();
}
// for 루프 버전
public static int rangeSum2(TreeMap<Integer, Integer> map, int from, int to) {
int sum = 0;
for (Integer value : map.subMap(from, true, to, true).values()) {
sum += value;
}
return sum;
}
}// 가장 큰 K개의 Entry
public class Problem3 {
public static Map<Integer, String> topK(TreeMap<Integer, String> map, int k) {
// 코드 작성
return null;
}
public static void main(String[] args) {
TreeMap<Integer, String> scores = new TreeMap<>();
scores.put(85, "Alice");
scores.put(92, "Bob");
scores.put(78, "Charlie");
scores.put(95, "David");
scores.put(88, "Eve");
Map<Integer, String> top3 = topK(scores, 3);
System.out.println("Top 3: " + top3);
// {95=David, 92=Bob, 88=Eve}
}
}정답:
정답 보기
public class Problem3 {
public static Map<Integer, String> topK(TreeMap<Integer, String> map, int k) {
Map<Integer, String> result = new LinkedHashMap<>();
int count = 0;
for (Map.Entry<Integer, String> entry : map.descendingMap().entrySet()) {
result.put(entry.getKey(), entry.getValue());
if (++count >= k) break;
}
return result;
}
// NavigableMap 버전
public static Map<Integer, String> topK2(TreeMap<Integer, String> map, int k) {
// 가장 큰 K번째 Key 찾기
int count = 0;
Integer kthKey = null;
for (Integer key : map.descendingKeySet()) {
if (++count == k) {
kthKey = key;
break;
}
}
if (kthKey == null) return map;
return new LinkedHashMap<>(map.tailMap(kthKey, true));
}
}// Key-Value + 자동 정렬
TreeMap<Integer, String> map = new TreeMap<>();
map.put(5, "Five");
map.put(2, "Two");
map.put(8, "Eight");
System.out.println(map); // {2=Two, 5=Five, 8=Eight}// 추가/삭제
map.put(5, "Five"); // O(log n)
map.remove(5); // O(log n)
// 첫/마지막
map.firstKey(); // 최소 Key
map.lastKey(); // 최대 Key
map.pollFirstEntry(); // 최소 + 제거
map.pollLastEntry(); // 최대 + 제거
// 근접 Key
map.lowerKey(5); // < 5
map.floorKey(5); // <= 5
map.ceilingKey(5); // >= 5
map.higherKey(5); // > 5
// 범위
map.subMap(3, 8); // [3, 8)
map.headMap(5); // [처음, 5)
map.tailMap(7); // [7, 끝]put: O(log n)
get: O(log n)
remove: O(log n)
firstKey: O(log n)
✅ Key 기준 자동 정렬
✅ 범위 쿼리
✅ 최소/최대 빠른 접근
✅ 순위 시스템
❌ 빠른 검색만 (HashMap)
❌ 순서 유지만 (LinkedHashMap)