데이터를 가장 빠르게 찾을 수 있는 방법은 무엇일까? 그것은 ‘해시맵(HashMap)‘이다. 해시맵은 배열처럼 데이터가 촘촘히 모여있지 않아도, 원하는 값을 상수 시간 O(1) 안에 찾아낼 수 있는 자료구조다.

핵심 구조

해시맵은 본질적으로 ‘키(Key)‘를 ‘배열의 인덱스(Index)‘로 매핑하는 구조이다.

  1. 해시 함수(Hash Function): 키값을 입력받아 배열의 인덱스로 변환한다.
  2. 버킷(Bucket) 배열: 데이터를 저장하는 물리적 공간이다.
  3. 충돌 해결(Collision Resolution): 같은 인덱스에 데이터가 들어오려 할 때 이를 해결하는 전략(체이닝 등).

직접 구현해보는 HongjaeMap

자바의 HashMap 라이브러리를 쓰지 않고, 내부 원리를 그대로 따르는 HongjaeMap을 직접 구현해보겠습니다.

public class HongjaeMap<K, V> {
    
    // 내부 데이터 저장용 노드 클래스
    private static class Node<K, V> {
        K key;
        V value;
        Node<K, V> next;

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

    private Node<K, V>[] buckets;
    private int capacity = 16; // 기본 용량

    @SuppressWarnings("unchecked")
    public HongjaeMap() {
        buckets = new Node[capacity];
    }

    // 해시 함수: 키의 hashCode를 배열 크기로 나머지 연산
    private int getBucketIndex(K key) {
        return Math.abs(key.hashCode()) % capacity;
    }

    // 데이터 삽입
    public void put(K key, V value) {
        int index = getBucketIndex(key);
        Node<K, V> head = buckets[index];

        // 키가 이미 존재하면 값 업데이트
        while (head != null) {
            if (head.key.equals(key)) {
                head.value = value;
                return;
            }
            head = head.next;
        }

        // 키가 없으면 새로운 노드를 만들어 앞에 추가 (Chaining)
        Node<K, V> newNode = new Node<>(key, value);
        newNode.next = buckets[index];
        buckets[index] = newNode;
    }

    // 데이터 조회
    public V get(K key) {
        int index = getBucketIndex(key);
        Node<K, V> head = buckets[index];

        while (head != null) {
            if (head.key.equals(key)) {
                return head.value;
            }
            head = head.next;
        }
        return null;
    }
}

구현의 핵심 포인트

  1. Node 클래스: 해시 충돌(같은 인덱스) 발생 시 데이터를 연결 리스트(Chaining) 방식으로 묶기 위해 next 포인터를 포함합니다.
  2. getBucketIndex: 자바의 hashCode()를 사용하여 키를 배열의 인덱스로 변환합니다. Math.abs를 사용하는 이유는 해시 코드가 음수일 경우를 방지하기 위함입니다.
  3. put 메서드: 같은 키가 있는지 리스트를 순회하며 확인합니다. 있다면 값을 갱신(Update)하고, 없다면 새로운 노드를 리스트의 맨 앞에 삽입합니다.
  4. get 메서드: 인덱스를 계산하여 해당 버킷의 연결 리스트를 탐색하고 키가 일치하는 값을 반환합니다.

이처럼 HongjaeMap은 자바 표준 라이브러리 없이도 해시맵의 근본 원리인 인덱스 직접 계산연결 리스트를 통한 충돌 해결을 완벽하게 수행합니다.