JAVA

[JAVA] 고급 Map 패턴 및 활용 예제

인생아 2024. 11. 6. 09:51
반응형

Java에서는 Map을 다양한 방식으로 활용할 수 있으며, 특히 고급 Map 패턴을 적용하면 효율적으로 데이터를 관리하고 조회할 수 있습니다. 이 글에서는 캐시(Cache) 구현에서 자주 사용되는 LRU(Least Recently Used) 캐시LFU(Least Frequently Used) 캐시 패턴, 한 키에 여러 값을 매핑할 수 있는 멀티맵(MultiMap) 구현, 그리고 TreeMap을 활용한 범위 기반 검색 기능에 대해 설명하고, 각각의 예제 코드와 함께 활용 방안을 소개하겠습니다. 이러한 패턴은 Java 고급 Map 기능을 사용하고자 할 때 매우 유용합니다.

캐시(Cache) 구현: LRU 캐시와 LFU 캐시

캐시는 자주 사용되는 데이터를 빠르게 조회하기 위해 메모리에 저장하는 임시 저장소입니다. LRU 캐시는 가장 오랫동안 사용하지 않은 데이터를 제거하며, LFU 캐시는 가장 적게 사용된 데이터를 제거하여 새로운 데이터를 삽입할 공간을 만듭니다. Java에서는 LinkedHashMap을 사용해 LRU 캐시를 구현할 수 있으며, PriorityQueueHashMap을 조합하여 LFU 캐시를 구현할 수 있습니다.

LRU 캐시 예제 (LinkedHashMap 활용)

Java의 LinkedHashMap은 삽입 순서 또는 접근 순서를 유지하기 때문에 LRU 캐시를 간단히 구현할 수 있습니다.

import java.util.LinkedHashMap;
import java.util.Map;

class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true); // accessOrder를 true로 설정
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }
}

public class LRUCacheExample {
    public static void main(String[] args) {
        LRUCache<Integer, String> lruCache = new LRUCache<>(3);
        lruCache.put(1, "A");
        lruCache.put(2, "B");
        lruCache.put(3, "C");
        System.out.println(lruCache); // {1=A, 2=B, 3=C}
        
        lruCache.get(1); // 1이 최근 접근됨
        lruCache.put(4, "D"); // 가장 오래된 2가 제거됨
        System.out.println(lruCache); // {3=C, 1=A, 4=D}
    }
}

위 예제에서는 LinkedHashMap의 removeEldestEntry 메서드를 오버라이드해 LRU 방식으로 캐시를 관리합니다. 캐시가 가득 찼을 때 가장 오래된 항목을 제거하여 새 항목을 추가할 공간을 확보합니다.

LFU 캐시 예제

LFU 캐시는 각 항목의 사용 빈도를 추적해, 가장 사용 빈도가 낮은 항목을 제거하는 방식입니다. 이를 구현하기 위해서는 HashMapPriorityQueue를 함께 사용하여 각 항목의 빈도수를 관리합니다.

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

class LFUCache<K, V> {
    private final int capacity;
    private final Map<K, V> cache;
    private final Map<K, Integer> frequency;
    private final PriorityQueue<K> minHeap;

    public LFUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>();
        this.frequency = new HashMap<>();
        this.minHeap = new PriorityQueue<>((k1, k2) -> frequency.get(k1) - frequency.get(k2));
    }

    public V get(K key) {
        if (!cache.containsKey(key)) return null;
        frequency.put(key, frequency.get(key) + 1);
        minHeap.remove(key);
        minHeap.offer(key);
        return cache.get(key);
    }

    public void put(K key, V value) {
        if (capacity == 0) return;
        if (cache.containsKey(key)) {
            cache.put(key, value);
            get(key); // 호출하여 빈도 업데이트
            return;
        }
        if (cache.size() == capacity) {
            K leastUsed = minHeap.poll();
            cache.remove(leastUsed);
            frequency.remove(leastUsed);
        }
        cache.put(key, value);
        frequency.put(key, 1);
        minHeap.offer(key);
    }
}

위의 LFU 캐시에서는 PriorityQueue를 사용해 빈도수가 가장 낮은 항목을 효율적으로 제거할 수 있습니다. get() 메서드가 호출될 때마다 빈도수를 증가시키고 우선순위 큐에서 재정렬하여 빈도가 낮은 항목을 관리합니다.

반응형

멀티맵(MultiMap) 구현하기 (한 키에 여러 값 매핑)

멀티맵(MultiMap)은 하나의 키여러 개의 값을 연결하는 Map의 확장 형태로, 일반적으로 한 키에 여러 값을 저장할 필요가 있는 경우 사용됩니다. HashMapListSet을 값으로 저장하여 MultiMap을 간단히 구현할 수 있습니다.

멀티맵 예제

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class MultiMap<K, V> {
    private final Map<K, List<V>> map = new HashMap<>();

    public void put(K key, V value) {
        map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
    }

    public List<V> get(K key) {
        return map.getOrDefault(key, new ArrayList<>());
    }
}

public class MultiMapExample {
    public static void main(String[] args) {
        MultiMap<String, String> multiMap = new MultiMap<>();
        multiMap.put("fruit", "apple");
        multiMap.put("fruit", "banana");
        multiMap.put("color", "blue");

        System.out.println(multiMap.get("fruit")); // [apple, banana]
        System.out.println(multiMap.get("color")); // [blue]
    }
}

위 예제는 put() 메서드를 통해 키에 여러 값을 추가할 수 있는 MultiMap을 구현합니다. 한 키에 여러 값을 매핑할 수 있으며, 이를 통해 다양한 용도로 멀티맵을 활용할 수 있습니다.

TreeMap으로 범위 기반 검색 기능 구현

TreeMap키의 순서를 유지하면서 범위 검색 기능을 제공합니다. 특히 subMap() 메서드를 사용해 특정 범위의 키를 빠르게 조회할 수 있습니다.

TreeMap 범위 검색 예제

import java.util.NavigableMap;
import java.util.TreeMap;

public class TreeMapRangeExample {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(10, "Ten");
        treeMap.put(20, "Twenty");
        treeMap.put(30, "Thirty");
        treeMap.put(40, "Forty");

        NavigableMap<Integer, String> rangeMap = treeMap.subMap(15, true, 35, true);
        System.out.println(rangeMap); // {20=Twenty, 30=Thirty}
    }
}

이 예제에서는 TreeMap을 사용해 범위 기반 검색을 구현했습니다. subMap() 메서드는 특정 키 범위에 해당하는 부분 집합을 반환하여, 효율적인 범위 조회가 가능합니다. 이는 정렬된 데이터를 빠르게 필터링하거나 검색할 때 유용합니다.

참고사이트

반응형