[F-Lab 멘토링 학습]

TreeMap과 LinkedHashMap의 차이와 사용예제

everydeveloper 2023. 10. 10. 22:42

TreeMap과 LinkedHashMap의 차이와 사용예제

TreeMap은 자바의 java.util 패키지에서 제공하는 맵(map) 기반의 자료구조입니다. 이것은 레드-블랙 트리(Red-Black Tree) 알고리즘을 기반으로 하며, 키-값 쌍을 정렬된 상태로 저장합니다. 이 정렬은 키에 따라 이루어지며, 키가 Comparable 인터페이스를 구현한 경우에는 그에 따라, 또는 별도의 Comparator 객체를 제공하여 정렬을 수행할 수 있습니다.

주요 특징

  1. 정렬: 키를 기준으로 오름차순 또는 내림차순으로 정렬합니다.
  2. 로그 시간 복잡도: 대부분의 연산 (add, remove, contains, get 등)에 O(log n)의 시간 복잡도를 가집니다.
  3. 키 중복 불가: 같은 키를 가진 여러 개의 엔트리를 저장할 수 없습니다. 새로운 값으로 갱신됩니다.
  4. Null 키 불가: 기본적으로 TreeMap은 null 키를 허용하지 않습니다. 하지만 Comparator를 사용하여 이를 우회할 수 있습니다.
  5. 스레드 안전하지 않음: 별도의 동기화 처리가 필요합니다.

사용 예

import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(3, "세");
        treeMap.put(1, "하나");
        treeMap.put(4, "넷");
        treeMap.put(2, "둘");

        for (Integer key : treeMap.keySet()) {
            System.out.println("키: " + key + ", 값: " + treeMap.get(key));
        }
    }
}

메소드 예시

  • put(K key, V value): 키-값 쌍을 추가
  • get(Object key): 지정한 키에 해당하는 값을 반환
  • remove(Object key): 지정한 키를 가진 엔트리를 제거
  • firstKey(): 가장 작은 키 반환
  • lastKey(): 가장 큰 키 반환

AWS SAA 인증을 가지고 있고 웹 개발 전문가로서 TreeMap을 어떻게 활용할 수 있을지 고민해보시면 좋을 것 같습니다. 예를 들어, 정렬이 필요한 캐시 메커니즘에 적용할 수 있으며, O(log n)의 효율적인 검색으로 빠른 데이터 접근이 가능합니다.

생성자

TreeMap을 생성할 때에는 여러가지 방식이 있습니다.

  1. 기본 생성자: 빈 맵을 생성하고 자연 순서에 따라 키를 정렬합니다.
  2. TreeMap<Integer, String> treeMap = new TreeMap<>();
  3. Comparator를 사용하는 생성자: 사용자가 정의한 정렬 로직에 따라 키를 정렬합니다.
  4. TreeMap<String, String> treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

서브맵

TreeMap은 부분 맵(sub-map)을 생성하는 기능도 제공합니다.

  • headMap(K toKey): 주어진 키보다 작은 모든 키-값 쌍으로 이루어진 서브맵을 반환합니다.
  • tailMap(K fromKey): 주어진 키보다 크거나 같은 모든 키-값 쌍으로 이루어진 서브맵을 반환합니다.
  • subMap(K fromKey, K toKey): 시작 키와 끝 키 사이의 모든 키-값 쌍으로 이루어진 서브맵을 반환합니다.

메소드

  • containsKey(Object key): 지정한 키가 맵에 존재하는지 확인합니다.
  • containsValue(Object value): 지정한 값이 맵에 존재하는지 확인합니다.
  • replace(K key, V value): 지정한 키의 값을 새 값으로 대체합니다.

성능 최적화

AWS SAA 인증과 웹 개발 전문성을 갖춘 분이라면, 분산 컴퓨팅 환경에서 TreeMap의 활용 방안도 생각해 볼 수 있을 것입니다. 예를 들어, 여러 서버에 분산된 데이터를 효율적으로 집계하고 정렬하기 위해 TreeMap을 활용할 수 있습니다. 또한, 데이터 구조의 효율성을 위해 AWS의 다양한 저장소 서비스와 연계할 수 있습니다.

주의사항

  • 멀티스레딩 환경에서의 사용: TreeMap은 기본적으로 스레드 안전하지 않습니다. 따라서 동시성을 고려해야 하는 상황에서는 Collections.synchronizedSortedMap 등을 사용하여 동기화를 해야 합니다.

이러한 정보들을 통해 TreeMap의 기능과 활용 방안을 더 깊게 이해할 수 있을 것입니다.

TreeMap과 Red-Black Tree

TreeMap이 내부적으로 사용하는 레드-블랙 트리(Red-Black Tree)는 일종의 이진 탐색 트리입니다. 레드-블랙 트리는 자동으로 균형을 유지하므로, 삽입, 삭제, 검색 작업이 항상 로그 시간에 이루어집니다. 이러한 성질은 TreeMap이 효율적으로 작동할 수 있게 해줍니다.

Entry 클래스

TreeMap에서 한 쌍의 데이터를 저장하는 단위는 Map.Entry라는 클래스를 사용합니다. 이 Entry는 키와 값, 그리고 트리에서의 위치 정보 등을 포함하고 있습니다.

Map.Entry<Integer, String> entry = treeMap.firstEntry();

Null 처리

TreeMap은 기본적으로 키에 null 값을 허용하지 않습니다. 이는 키의 순서를 정렬해야 하는 TreeMap의 특성상, null 값이 어떤 위치에 와야 할지 결정할 수 없기 때문입니다.

내림차순

TreeMap은 descendingMap() 메소드를 통해 내림차순으로 정렬된 맵을 가져올 수 있습니다.

NavigableMap<Integer, String> descendingMap = treeMap.descendingMap();

시간 복잡도와 공간 복잡도

TreeMap의 시간 복잡도는 일반적으로 O(log n)입니다. 그리고 메모리 공간은 n개의 노드를 저장해야 하므로 O(n)의 공간을 차지합니다.

실제 활용 사례

TreeMap은 주로 범위 검색이나 정렬된 데이터가 필요한 경우에 사용됩니다. 예를 들어, 시계열 데이터를 처리할 때, 특정 시간 범위에 속하는 데이터만 빠르게 추출할 수 있습니다. 또한, 우선순위에 따라 데이터를 처리해야 하는 작업 큐를 구현하는 데에도 유용합니다.

AWS 환경에서의 활용

AWS SAA 자격을 가지고 있다면, TreeMap을 AWS의 다양한 서비스와 어떻게 연계할 수 있을지 고민해보는 것도 좋습니다. 예를 들어, 분산 데이터 저장 시스템에서 TreeMap을 사용하여 빠른 데이터 검색을 지원하거나, 람다 함수와 함께 사용하여 빠르게 데이터를 처리하는 등의 활용이 가능합니다.

이러한 정보를 바탕으로 TreeMap의 깊은 이해와 실용적인 활용이 가능하리라 생각됩니다.

트리 맵 - 시각적 표시

TreeMap을 시각적으로 표현하려면, 키-값 쌍이 레드-블랙 트리의 형태로 구성되어 있음을 이해해야 합니다. 이진 트리 형태의 자료구조에서 각 노드는 키와 값, 그리고 왼쪽 자식과 오른쪽 자식에 대한 참조를 갖고 있습니다. 레드-블랙 트리에서는 추가로 노드 색상 (레드 또는 블랙)도 가집니다.

간단한 예를 들면, 아래와 같은 키-값 쌍이 있는 TreeMap을 고려해보겠습니다.

  • (1, "하나")
  • (3, "세")
  • (5, "다섯")
  • (7, "일곱")
  • (9, "아홉")

이를 레드-블랙 트리로 표현한다면 다음과 같은 형태를 가질 수 있습니다. (B는 블랙 노드, R은 레드 노드를 의미합니다.)

       (5, "다섯") B
     /                \\\\
  (3, "세") R       (9, "아홉") R
 /       \\\\                /       \\\\
(1, "하나") B   (7, "일곱") B

위 트리에서는 다음과 같은 규칙이 적용됩니다.

  1. 루트 노드는 블랙입니다.
  2. 레드 노드의 자식 노드는 블랙입니다. (즉, 레드 노드가 연속으로 올 수 없습니다.)
  3. 루트 노드부터 모든 리프 노드까지의 경로에서 블랙 노드의 수는 같습니다.

이러한 방식으로 TreeMap은 내부적으로 레드-블랙 트리를 구성하고, 이를 통해 O(log n)의 시간 복잡도로 데이터의 삽입, 삭제, 검색을 처리합니다.

시각적으로 표현하면 더 복잡한 트리 구조도 가능하겠지만, 기본적인 아이디어는 위와 같습니다. 이를 이해하면 TreeMap의 동작 원리와 효율성을 더 깊게 이해할 수 있을 것입니다.

이진 트리 & 트리 맵

이진 트리와 TreeMap은 서로 밀접한 관계를 가지고 있습니다. 이진 트리는 노드가 최대 두 개의 자식 노드를 가지는 트리 구조를 말하며, 이진 트리의 여러 가지 변형 중 하나가 레드-블랙 트리입니다. TreeMap은 이 레드-블랙 트리를 내부적으로 사용하여 키-값 쌍을 저장하고 관리합니다.

이진 트리의 기본 개념

  • 노드: 키와 값, 왼쪽과 오른쪽 자식 노드에 대한 참조를 가짐
  • 루트 노드: 트리의 최상위 노드
  • 리프 노드: 자식 노드가 없는 노드

레드-블랙 트리의 특징

  • 모든 노드는 레드 또는 블랙 중 하나의 색을 가짐
  • 루트 노드는 블랙
  • 레드 노드의 자식 노드는 무조건 블랙
  • 루트 노드부터 모든 리프 노드까지의 각 경로에 포함된 블랙 노드의 수는 동일

TreeMap의 작동 원리

  1. 삽입: 새로운 키-값 쌍이 삽입되면, 레드-블랙 트리의 규칙을 유지하면서 적절한 위치에 노드가 삽입됩니다.
  2. 삭제: 특정 키를 가진 노드를 삭제할 때도 레드-블랙 트리의 규칙을 유지합니다.
  3. 검색: 특정 키를 가진 노드를 찾을 때, 이진 트리의 특성을 활용해 로그 시간 복잡도로 검색이 가능합니다.

시간 복잡도

  • 삽입, 삭제, 검색 작업은 모두 O(log n)의 시간 복잡도를 가집니다.

이러한 내부 구조 덕분에 TreeMap은 키를 기준으로 정렬된 상태를 유지하면서도 빠른 작동 속도를 보장합니다. 이진 트리 구조와 레드-블랙 트리의 특성을 이해하면 TreeMap의 동작 원리를 더 깊게 이해할 수 있습니다.

레드 블랙 트리

레드-블랙 트리(Red-Black Tree)는 이진 탐색 트리의 일종으로, 균형 잡힌 트리를 유지하는 특별한 규칙을 가집니다. 이 균형을 유지함으로써 트리에 대한 여러 작업의 시간 복잡도가 O(log n)으로 보장됩니다. 자바의 TreeMap이나 C++의 std::map 같은 데이터 구조에서 내부적으로 사용됩니다.

규칙

  1. 노드 색상: 각 노드는 레드 또는 블랙 중 하나의 색상을 가집니다.
  2. 루트 노드: 트리의 루트 노드는 블랙입니다.
  3. 레드 노드의 자식: 만약 노드가 레드라면, 그 노드의 자식 노드는 모두 블랙이어야 합니다. (레드 노드가 연속으로 올 수 없습니다.)
  4. 새로운 삽입: 새로운 노드는 레드로 삽입됩니다. 그 후 레드-블랙 트리의 규칙을 만족하도록 조정합니다.
  5. 블랙 높이: 모든 리프 노드(NULL 노드)로 가는 경로에는 동일한 개수의 블랙 노드가 있어야 합니다.

삽입과 삭제

  • 삽입: 새 노드는 레드로 삽입되고, 이후에 규칙을 위반하지 않도록 트리를 재배열합니다. 필요한 경우 회전 연산을 사용하여 트리의 균형을 유지합니다.
  • 삭제: 삭제 후에도 트리의 균형을 유지해야 하며, 이 과정에서도 회전과 색상 변경이 일어날 수 있습니다.

시간 복잡도

  • 삽입, 삭제, 검색 작업은 O(log n)의 시간 복잡도를 가집니다.

장점과 단점

  • 장점: 균형이 잡힌 트리이므로 빠른 검색, 삽입, 삭제가 가능합니다.
  • 단점: 노드에 색상 정보를 저장해야 하므로 메모리 사용량이 약간 늘어납니다. 또한, 삽입과 삭제 시에 규칙을 유지하기 위한 추가 작업이 필요합니다.

레드-블랙 트리는 이러한 규칙과 특성 때문에 균형 잡힌 성능을 보이며, 다양한 컴퓨터 과학 문제에서 활용됩니다.

트리 맵 java 예시 코드

Java에서 TreeMap을 사용하는 간단한 예제 코드를 아래에 제시합니다. 이 예제에서는 TreeMap에 정수 키와 문자열 값이 저장되고, 기본적인 연산들이 수행됩니다.

import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        // TreeMap 인스턴스 생성
        TreeMap<Integer, String> treeMap = new TreeMap<>();

        // 키-값 쌍 추가
        treeMap.put(3, "세");
        treeMap.put(1, "하나");
        treeMap.put(4, "네");
        treeMap.put(2, "둘");

        // 출력 (정렬된 순서대로 출력됨)
        System.out.println("전체 TreeMap: " + treeMap);

        // 특정 키에 대한 값을 얻기
        System.out.println("키 3에 대한 값: " + treeMap.get(3));

        // 가장 작은 키와 그에 대한 값을 얻기
        System.out.println("가장 작은 키: " + treeMap.firstKey());
        System.out.println("가장 작은 키의 값: " + treeMap.get(treeMap.firstKey()));

        // 특정 키 삭제
        treeMap.remove(2);

        // 삭제 후 출력
        System.out.println("키 2 삭제 후 TreeMap: " + treeMap);
    }
}

출력 결과

전체 TreeMap: {1=하나, 2=둘, 3=세, 4=네}
키 3에 대한 값: 세
가장 작은 키: 1
가장 작은 키의 값: 하나
키 2 삭제 후 TreeMap: {1=하나, 3=세, 4=네}

이 예제는 매우 기본적인 사용 예입니다. TreeMap은 키를 기준으로 자동 정렬되며, 다양한 메서드를 제공해 복잡한 연산도 쉽게 수행할 수 있습니다. Java의 Collections Framework에 속해 있어 다양한 인터페이스와 잘 호환되기도 합니다.

트리맵 자료구조 java 코드

Java의 TreeMap은 라이브러리로 이미 구현되어 있지만, 이를 직접 구현하려면 대개 레드-블랙 트리를 기반으로 코드를 작성하게 됩니다. 아래는 매우 간단한 TreeMap의 레드-블랙 트리 구현 예시입니다. 이 예시에서는 키와 값을 저장하는 Node 클래스와 기본적인 put과 get 메서드만을 구현하였습니다.

public class MyTreeMap<K extends Comparable<K>, V> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private class Node {
        K key;
        V value;
        Node left, right;
        boolean color;

        Node(K key, V value) {
            this.key = key;
            this.value = value;
            this.color = RED;
        }
    }

    private Node root;

    private boolean isRed(Node x) {
        return x != null && x.color == RED;
    }

    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }

    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }

    private void flipColors(Node h) {
        h.color = !h.color;
        h.left.color = !h.left.color;
        h.right.color = !h.right.color;
    }

    public void put(K key, V value) {
        root = put(root, key, value);
        root.color = BLACK;
    }

    private Node put(Node h, K key, V value) {
        if (h == null) return new Node(key, value);

        int cmp = key.compareTo(h.key);
        if (cmp < 0) h.left = put(h.left, key, value);
        else if (cmp > 0) h.right = put(h.right, key, value);
        else h.value = value;

        if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
        if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left) && isRed(h.right)) flipColors(h);

        return h;
    }

    public V get(K key) {
        Node x = root;
        while (x != null) {
            int cmp = key.compareTo(x.key);
            if (cmp < 0) x = x.left;
            else if (cmp > 0) x = x.right;
            else return x.value;
        }
        return null;
    }

    public static void main(String[] args) {
        MyTreeMap<Integer, String> myTreeMap = new MyTreeMap<>();
        myTreeMap.put(1, "하나");
        myTreeMap.put(2, "둘");
        myTreeMap.put(3, "셋");

        System.out.println(myTreeMap.get(1));  // 출력: 하나
        System.out.println(myTreeMap.get(2));  // 출력: 둘
        System.out.println(myTreeMap.get(3));  // 출력: 셋
    }
}

이 코드는 교육적 목적으로 작성되었으며, 실제 상용 환경에서는 Java의 내장 TreeMap 클래스를 사용하는 것이 더 효율적입니다. 위의 예시는 레드-블랙 트리의 기본 원칙을 따르면서 put과 get 기능을 간단하게 구현하였습니다.

TreeMap과 LinkedHashMap의 차이

TreeMap과 LinkedHashMap은 둘 다 Java의 Map 인터페이스를 구현한 클래스이지만, 다양한 차이점이 있습니다. 주요 차이점을 아래에 정리해보겠습니다.

TreeMap

  1. 정렬: 자동으로 키를 정렬합니다. 키의 정렬은 키의 자연 순서나 명시적으로 제공된 Comparator에 따릅니다.
  2. 시간 복잡도: 기본적인 연산(get, put, remove 등)의 시간 복잡도는 O(log N)입니다.
  3. Null 허용: 키에 null을 허용하지 않습니다.
  4. 내부 구현: 레드-블랙 트리를 이용한 이진 검색 트리로 구현됩니다.
  5. 추가 기능: 키의 부분 집합이나 범위 검색 같은 추가적인 연산을 지원합니다.

LinkedHashMap

  1. 정렬: 키의 삽입 순서 또는 접근 순서에 따라 엔트리를 유지합니다. 따라서 자동으로 키를 정렬하지 않습니다.
  2. 시간 복잡도: 기본적인 연산(get, put, remove 등)의 시간 복잡도는 일반적으로 O(1)입니다.
  3. Null 허용: 키와 값에 null을 허용합니다.
  4. 내부 구현: 해시 테이블과 더블 링크드 리스트로 구현됩니다.
  5. 추가 기능: 삽입 순서나 접근 순서에 따른 순회가 가능하지만, TreeMap처럼 범위 검색이나 부분 집합에 대한 연산은 지원하지 않습니다.

사용 사례

  • TreeMap은 키의 순서가 중요하거나, 범위 검색과 같은 추가적인 연산이 필요한 경우에 사용됩니다.
  • LinkedHashMap은 삽입 순서나 접근 순서가 중요한 경우, 예를 들어 캐시 구현,에 주로 사용됩니다.

두 자료구조는 서로 다른 상황과 요구 사항에 따라 적합하므로, 사용할 문맥에 따라 적절한 자료구조를 선택해야 합니다.

차이 간단 요약

  • TreeMap: 자동으로 키를 정렬하며, 일반적으로 기본 연산 (get, put, remove 등)의 시간 복잡도는 O(log N)입니다.
  • LinkedHashMap: 키의 삽입 순서나 최근 접근 순서를 유지하며, 일반적으로 기본 연산 (get, put, remove 등)의 시간 복잡도는 O(1)입니다.

TreeMap은 정렬된 키를 원하거나 범위 검색 등 추가적인 연산이 필요한 경우에 적합하고, LinkedHashMap은 키의 삽입 순서나 접근 순서를 유지해야 하는 경우에 적합합니다. 따라서 사용 상황에 따라 적절한 자료구조를 선택할 수 있습니다.