Rei’s Tech diary

Map - LinkedHashMap 이론과 구현 본문

프로그래밍/Data Structure

Map - LinkedHashMap 이론과 구현

Reiger 2025. 1. 2. 00:02

 

 

📌 LinkedHashMap<K, V>

- HashMap<K, V>와 동일한 기능 수행

- 력 순서와 출력순서가 같다.

 


 

📌 Map<K, V> 컬렉션의 주요 메서드

Map<K, V>의 구현 클래스는 아래의 모든 메서드를 포함한다.

구분 리턴타입 메서드 이름 기능
데이터
추가
V put(K key, V value) 입력 매개변수(키,값)을 Map객체에 추가
void putAll(Map<? extends K, ? extends V> m) 입력매개변수의 Map객체를 통제로 추가
데이터
변경
V replace(K key, V value) Key 값에 해당하는 값을 value로 변경 (old값 리턴)
(단, 해당 key가 없으면 null 리턴)
boolean replace(K key, V oldValue, V newValue) (key, oldValue)의 쌍을 가지는 엔트리에서 값을 newValue로 변경
(단, 해당 엔트리가 없으면 false를 리턴)
데이터
정보추출
V get(Object key) 매개변수의 key값에 해당하는 valuye 값을 리턴
boolean containsKey(Object key) 매개변수의 key값이 포함되어 있는지 여부
boolean containsValue(Object value) 매개변수의 value 값이 포함되어 있는지 여부
Set<K> keySet() Map 데이터들 중 key들만 뽑아서 Set객체로 리턴
Set<Entry<K,V>> entrySet() Map의 각 Entry들을 Set 객체로 담아 리턴
int size() Map에 포함된 데이터(Entry) 개수
데이터
삭제
V remove(Object key) 입력 매개변수의 Key를 갖는 엔트리 삭제
(단, 해당 key가 없으면 아무런 동작을 안함)
boolean remove(Object key, Object value) 입력 배개변수의 (key, value)를 갖는 엔트리 삭제
(단, 해당 엔트리가 없으면 아무런 동작 안함)
void clear() Map 객체내의 모든 데이터 삭제

 

 

예시코드)

public class LinkedHashMapMethod {
    public static void main(String[] args) {
        Map<Integer, String> lhMap1 = new LinkedHashMap<>();

        //#1. put (K key, V value)
        lhMap1.put(1, "나다라");
        lhMap1.put(2, "가나다");
        lhMap1.put(3, "다라마");
        System.out.println(lhMap1.toString()); //{1=나다라, 2=가나다, 3=다라마}

        //#2. putAll(다른객체)
        Map<Integer, String> lhMap2 = new LinkedHashMap<>();
        lhMap2.putAll(lhMap1);
        System.out.println(lhMap2.toString()); //{1=나다라, 2=가나다, 3=다라마}

        //#3. replace(K key, V value)
        lhMap2.replace(1, "가가가");
        lhMap2.replace(4, "라라라"); //동작안함
        System.out.println(lhMap2.toString()); //{1=가가가, 2=가나다, 3=다라마}

        //#4. replace(K key, V oldValue, V new Value)
        lhMap2.replace(1, "가가가", "나나나");
        lhMap2.replace(2, "다다다", "라라라"); //동작안함
        System.out.println(lhMap2); //{1=나나나, 2=가나다, 3=다라마}

        //#5. v get(Object key)
        System.out.println(lhMap2.get(1)); //나나나
        System.out.println(lhMap2.get(2)); //가나다
        System.out.println(lhMap2.get(3)); //다라마

        //#6. containsKey(Object key)
        System.out.println(lhMap2.containsKey(1)); //true
        System.out.println(lhMap2.containsKey(5)); //false

        //#7. containsValue(Object value)
        System.out.println(lhMap2.containsValue("나나나")); //true
        System.out.println(lhMap2.containsValue("다다다")); //false

        //#8. Set<K> keySet()
        Set<Integer> keySet = lhMap2.keySet();
        System.out.println(keySet.toString()); //[1, 2, 3]

        //#9. Set<Map.Entry<K,V>> entrySet()
        Set<Map.Entry<Integer, String>> entrySet = lhMap2.entrySet();
        System.out.println(entrySet.toString()); //[1=나나나, 2=가나다, 3=다라마]

        //#10. size()
        System.out.println(lhMap2.size()); //3

        //#11. remove(Object key)
        lhMap2.remove(1);
        lhMap2.remove(4); //동작안함
        System.out.println(lhMap2.toString()); //{2=가나다, 3=다라마}

        //#12. remove(Object key, Object value)
        lhMap2.remove(2, "가나다");
        lhMap2.remove(3, "다다다"); //동작안함
        System.out.println(lhMap2.toString()); //{3=다라마}

        //#13. clear()
        lhMap2.clear();
        System.out.println(lhMap2.toString());//{}
    }
}

 

 


 

💡 LinkedHashMap 구현

 

import java.util.HashMap;

public class MyLinkedHashMap<K, V>{
    private static class Entry<K, V>{
        K key;
        V value;
        Entry<K, V> prev;
        Entry<K, V> next;

        Entry(K key, V value){
            this.key = key;
            this.value = value;
        }
    }

    private final HashMap<K, Entry<K, V>> map;
    private Entry<K, V> head;
    private Entry<K, V> tail;
    private int size;

    public MyLinkedHashMap(){
        this.map = new HashMap<>();
        this.size = 0;
    }
    
    public void put(K key, V value){
        if(map.containsKey(key)){
            Entry<K, V> entry = map.get(key);
            entry.value = value;
            moveToTail(entry);
        }else{
            Entry<K, V> entry = new Entry<>(key, value);
            addToTail(entry);
            map.put(key, entry);
            size++;
        }
    }

    public V get(K key){
        if(!map.containsKey(key)){
            return null;
        }
        Entry<K, V> entry = map.get(key);
        return entry.value;
    }

    public void remove(K key){
        if(!map.containsKey(key)){
            return;
        }
        Entry<K, V> entry = map.get(key);
        removeNode(entry);
        map.remove(key);
        size--;
    }
    
    public int size(){
        return size;
    }

    private void removeNode(Entry<K, V> entry){
        if(entry.prev != null){
            entry.prev.next = entry.next;
        }else{
            head = entry.next;
        }

        if(entry.next != null){
            entry.next.prev = entry.prev;
        }else{
            tail = entry.prev;
        }
    }

    private void addToTail(Entry<K, V> entry){
        if(tail == null){
            head = tail = entry;
        } else {
            tail.next = entry;
            entry.prev = tail;
            tail = entry;
        }
    }

    private void moveToTail(Entry<K, V> entry){
        removeNode(entry);
        addToTail(entry);
    }

    public void print(){
        Entry<K, V> current = head;
        while(current != null){
            System.out.print(current.key + "=" + current.value + " ");
            current = current.next;
        }
        System.out.println();
    }


}

 

 

필드, 메서드, 생성자, 내부 클래스

 

- Entry 클래스: 개별 항목을 저장하며 이전/다음 노드를 가리키는 구조

private static class Entry<K, V> {
    K key; // 항목의 키
    V value; // 항목의 값
    Entry<K, V> prev; // 이전 항목에 대한 참조
    Entry<K, V> next; // 다음 항목에 대한 참조

    // Entry 생성자: 키와 값을 초기화
    Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

private final HashMap<K, Entry<K, V>> map; // 항목을 빠르게 조회하기 위한 HashMap
private Entry<K, V> head; // 연결 리스트의 첫 번째 항목
private Entry<K, V> tail; // 연결 리스트의 마지막 항목
private int size; // 항목의 총 개수

public MyLinkedHashMap() {
    this.map = new HashMap<>();
    this.size = 0;
}

 

 


 

key - value 쌍 저장 (put)

 

public void put(K key, V value) {
    if (map.containsKey(key)) {
        // 키가 이미 존재하면 값을 업데이트하고 리스트 위치 갱신
        Entry<K, V> entry = map.get(key);
        entry.value = value; // 값 업데이트
        moveToTail(entry); // 최신 항목으로 이동
    } else {
        // 키가 없으면 새로운 항목 추가
        Entry<K, V> entry = new Entry<>(key, value);
        addToTail(entry); // 리스트 끝에 추가
        map.put(key, entry); // HashMap에 저장
        size++; // 항목 개수 증가
    }
}


// removeNode 메서드: 리스트에서 특정 노드 제거
private void removeNode(Entry<K, V> entry) {
    if (entry.prev != null) {
        entry.prev.next = entry.next; // 이전 노드의 next를 현재 노드의 next로 설정
    } else {
        head = entry.next; // 첫 번째 노드 제거 시 head 업데이트
    }

    if (entry.next != null) {
        entry.next.prev = entry.prev; // 다음 노드의 prev를 현재 노드의 prev로 설정
    } else {
        tail = entry.prev; // 마지막 노드 제거 시 tail 업데이트
    }
}

// addToTail 메서드: 새로운 노드를 리스트의 끝에 추가
private void addToTail(Entry<K, V> entry) {
    if (tail == null) {
        // 리스트가 비어있으면 head와 tail 모두 설정
        head = tail = entry;
    } else {
        // 기존 tail 뒤에 새로운 항목 추가
        tail.next = entry;
        entry.prev = tail;
        tail = entry; // tail 업데이트
    }
}

// moveToTail 메서드: 특정 노드를 리스트의 끝으로 이동
private void moveToTail(Entry<K, V> entry) {
    removeNode(entry); // 기존 위치에서 제거
    addToTail(entry); // 리스트 끝에 다시 추가
}

 

 


키에 해당하는 값 반환 (get)

 

public V get(K key) {
    if (!map.containsKey(key)) {
        return null; // 키가 없으면 null 반환
    }
    Entry<K, V> entry = map.get(key); // HashMap에서 항목 가져오기
    return entry.value; // 값 반환
}

 

 


 

특정 키를 가진 요소 제거 (remove)

 

public void remove(K key) {
    if (!map.containsKey(key)) {
        return; // 키가 없으면 아무 작업도 하지 않음
    }
    Entry<K, V> entry = map.get(key); // HashMap에서 항목 가져오기
    removeNode(entry); // 리스트에서 항목 제거
    map.remove(key); // HashMap에서 항목 제거
    size--; // 항목 개수 감소
}

 

 


 

요소 갯수 반환 size

 

public int size() {
    return size;
}

 

 


 

입력 순서대로 출력

 

public void print() {
    Entry<K, V> current = head; // 리스트의 첫 번째 항목부터 시작
    while (current != null) {
        System.out.print(current.key + "=" + current.value + " "); // 키=값 출력
        current = current.next; // 다음 항목으로 이동
    }
    System.out.println(); // 줄바꿈
}

'프로그래밍 > Data Structure' 카테고리의 다른 글

Stack 이론과 구현  (0) 2025.01.02
Map - TreeMap 이론과 구현  (0) 2025.01.02
Map - HashTable 이론과 구현  (2) 2025.01.01
Map - HashMap 이론과 구현  (2) 2024.12.30
Set - TreeSet 이론과 구현  (0) 2024.12.28