카테고리 없음

자바 공부#2_05. Map

코딩연습중 2025. 4. 6. 15:09

Map?

Map 인터페이스는 Java 컬렉션 프레임워크의 주요 인터페이스지만, List & Set 인터페이스와는 다르게 Collection 인터페이스를 상속받지는 않는다. Map 인터페이스는 Collection 인터페이스와는 다른 저장 방식을 갖기 때문이다. 

Map 인터페이스를 구현한 Map 컬렉션 클래스들은 key와 value를 하나의 쌍으로 저장하는 방식(key-value)을 사용한다.

Map 특징

  1. 키를 통해 값을 조회하는 구조(key-value)
  2. 키는 중복 불가, 값은 중복 허용 ✅ (동일한 key 저장 시 값이 덮어쓰기 됨)
  3. 순서 보장 여부는 클래스에 따라 다름
  4. null 허용 여부는 클래스에 따라 다름
    (예: HashMap은 null 키 1개, null 값 여러 개 허용 / Hashtable은 둘 다 허용 ❌)

Queue인터페이스를 구현한 대표적인 클래스에는 HashMap, LinkedHashMap, TreeMap, HashTable,  ConcurrentHashMap 등이 있다.

 


 

 

1. HashMap

HashMap은 가장 일반적으로 사용되는 Map 컬렉션 클래스다. 내부적으로 해시 테이블을 사용해 key의 hashCode() 값을 기반으로 데이터를 저장하고 조회하기 때문에 데이터 검색 속도가 매우 빠르다. key-value 쌍으로 데이터를 저장하며, 순서는 전혀 보장하지 않는다.

특징

  • 순서 보장 ❌
  • 검색, 삽입, 삭제 속도 빠름 (평균 O(1))
  • null key/value 허용
  • 추가, 삭제, 검색, 접근성이 모두 뛰어남
  • 비동기로 작동하기 때문에 멀티스레드 안전 ❌ → ConcurrentHashMap 사용

중복 없는 key로 데이터를 빠르게 조회하고 저장해야 할 때 사용.
ex) 상품ID → 상품정보 매핑, 사용자ID → 사용자설정 등

 

2. LinkedHashMap

LinkedHashMap은 HashMap과 동일한 해시 기반 구조이지만, Entry들이 연결 리스트를 구성하여 입력 순서(삽입 순서)를 유지한다. 내부적으로 더블 링크드 리스트를 이용해 요소 간의 순서를 기억한다.

특징

  • 입력 순서 유지 ✅
  • 기본 연산 속도는 HashMap과 유사 (약간 느림)
  • null key/value 허용
  • 순서 중요할 경우 적합
  • 정렬은 불가능 (단순 순서 유지)

데이터 입력 순서를 유지하면서도 해시 기반의 빠른 접근이 필요한 경우
ex) 최근 검색어 목록, 캐시 구현 (LRU 기반으로 사용하기도 함)

 

3. TreeMap

TreeMap은 이진 탐색 트리(Red-Black Tree) 기반의 Map으로, 키와 값의 쌍으로 이루어진 데이터를 저장한다(TreeSet과 같은 원리). TreeMap은 SortedMap 인터페이스를 구현하고 있어 key 기준으로 자동 정렬(오름차순)된다. Comparable 인터페이스 구현 또는 Comparator 제공을 통해 사용자 정의 정렬도 가능하다. 또한 정렬된 순서로 키/값 쌍을 저장하므로 빠른 검색(특히 범위 검색)이 가능하다.

특징

  • 정렬된 key 순서 유지 ✅
  • 검색, 삽입, 삭제 O(log n) — 트리 탐색 기반
  • 키와 값을 저장하는 동시에 정렬을 하기 때문에 저장시간이 다소 오래 걸림
  • null key ❌ (value는 가능)
  • 범위 검색, 정렬 기반 탐색 등에 적합
  • 순서 유지 + 정렬 가능

key를 정렬된 상태로 관리해야 하는 경우.
ex) 사용자 이름 순, 시간 순 정렬, 구간 탐색 등

 

4. HashTable

HashTable은 자바 초기 버전에 나온 레거시 클래스로, HashMap보다는 느리지만 동기화가 지원된다. 멀티스레드 환경에서 안전하지만, 내부적으로 객체 단위로 락(lock)을 걸어 성능이 떨어진다. 이로 인해 현재는 ConcurrentHashMap을 주로 사용한다.

특징

  • 순서 보장 ❌
  • null key ❌ / null value ❌
  • 멀티스레드 환경에서 thread-safe ✅
  • 동기화로 인해 성능 저하 발생 ❌ (비교적 느림)

이전 레거시 코드에서만 사용되는 경우가 많으며, 새로운 개발에서는 ConcurrentHashMap 사용을 권장.

 

5. ConcurrentHashMap

ConcurrentHashMap은 Hashtable 클래스의 단점을 보완하면서 Multi-Thread 환경에서 사용할 수 있도록 나온 클래스이기 때문에 멀티스레드 환경에서 동기화(Synchronization) 처리가 되어 있는 Map이다. 읽기 작업에는 여러 쓰레드가 동시에 읽을 수 있지만, 쓰기 작업에는 특정 Segment 또는 Bucket 단위로 락(lock)을 걸어 성능 저하를 최소화한다.

특징

  • 멀티스레드 환경에서 안전하게 동작 ✅
  • 성능과 동기화를 모두 고려
  • key/value null 허용 ❌ (둘 다 저장 불가)
  • 정렬 및 순서 보장 ❌
  • 최신 Java에서는 synchronizedMap보다 선호됨

멀티스레드 환경에서 여러 쓰레드가 동시에 읽고 쓰는 상황
ex) 실시간 사용자 세션 관리, API 요청 수 카운팅 등

 

 

6. HashMap vs Hashtable vs ConcurrentHashMap

  HashMap HashTable ConcurrentHashMap
동기화(Synchronized) ❌ (비동기) ✅ (기본 동기화) ✅ (세분화된 동기화, 효율적)
멀티스레드 환경 ❌ Thread-safe 아님 ✅ 가능하지만 느림 ✅ 고성능 Thread-safe
null key 1개 허용 ❌ 허용 안 됨 ❌ 허용 안 됨
null value 여러 개 허용 ❌ 허용 안 됨 ❌ 허용 안 됨
성능 가장 빠름 (단일 스레드 기준) 느림 (전체 동기화) 중간 (병렬처리로 최적화됨)
사용 시기 단일 스레드 환경 과거 레거시 코드 멀티스레드 환경에서 선호
등장 시기 Java 1.2 Java 1.0 Java 1.5

 

결론

  • 단일 스레드 환경에서 성능이 중요하면 → HashMap
  • 오래된 코드 유지보수 중이면 → Hashtable (새로운 개발엔 비추)
  • 멀티스레드 환경에서 안정성과 성능 둘 다 잡고 싶다면 → ConcurrentHashMap

 


 

Map에서 반복문(for-each)

Map은 말 그대로 (key, value) 쌍을 저장하는 자료구조고, Collection 인터페이스를 구현하지도 않고, Iterable도 직접 구현하지 않기 때문에 바로 for-each문을 사용할 수는 없다. 하지만 Map에서 keySet(), values(), entrySet() 같은 메서드를 통해 간접적으로 반복문을 쓸 수 있다. 그 중 entrySet() 사용이 가장 일반적이고 효율적이다.

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}

 

 

Map 주요 구현 클래스 비교 정리

구분 HashMap LinkedHashMap TreeMap Hashtable ConcurrentHashMap

  HashMap  LinkedHashMap  TreeMap  Hashtable  ConcurrentHashMap
정렬 여부 ❌ 없음 ✅ 입력 순서 유지 ✅ 정렬 순서 유지 (기본 오름차순) ❌ 없음 ❌ 없음
동기화 여부 ❌ 비동기 ❌ 비동기 ❌ 비동기 ✅ 동기화 ✅ 동시성 지원 (세분화된 락)
null 허용 key 1개, value 여러 개 허용 key 1개, value 여러 개 허용 ❌ 허용 안함 ❌ 허용 안함 ❌ 허용 안함
내부 구조 배열 + 해시 해시 + 이중 연결 리스트 이진 탐색 트리 (Red-Black Tree) Hashtable 구조 세그먼트 기반 해시
성능 빠름 (O(1)) 약간 느림 (순서 추적) 느림 (O(log n)) 느림 빠름 (락 세분화)
스레드 안정성 ❌ 비스레드 안전 ❌ ❌ ✅ ✅

 

❗ 사용 시 주의점 

HashMap

  • 멀티스레드 환경에서는 사용 지양
  • equals(), hashCode() 오버라이딩 필수 (중복 방지 및 정확한 저장/조회 위해)

TreeMap

  • Comparable 구현 또는 Comparator 전달 필수

 

 


 

💡EnumMap?

공부하다 보니까 EnumMap이라는 Map 인터페이스의 구현 클래스가 존재한다는 것을 알게 됐다. 

EnumMap은 Map 인터페이스에서 키를 특정 enum 타입만 사용하도록 하는 구현체이다.

그리고 enum은 내부적으로 ordinal() 값을 갖고있다.

Color.RED.ordinal(); // 0
Color.GREEN.ordinal(); // 1
Color.BLUE.ordinal(); // 2

즉, EnumMap은 enu의 ordinal 값을 그대로 배열의 인덱스처럼 사용해서 값을 저장한다.

[ "redValue", "greenValue", "blueValue" ]

 

장점

  • 해시 충돌 걱정이 없음 → hashCode() 안 씀
  • 메모리 접근 빠름(배열은 연속된 메모리 → 탐색 속도 ↑)
  • 연산 간단 → 속도 빠름 & 가볍다

Enum vs HashMap

  • EnumMap은 해시 충돌 ❌
  • EnumMap은 키가 순서대로 표시, HashMap은 순서를 보장하지는 못함
  • HashMap은 일정 이상의 자료가 들어오면 자체적으로 resizing을 하지만 EnumMap은 enum이 가지고있는 상수 객체의 수가 정해져있기 때문에 크기가 변하거나 하지 않는다

 

참고:

- https://incodom.kr/Java/java.util.Map

- https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC#map_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4

- https://devlog-wjdrbs96.tistory.com/269

EnumMap 관련

- https://www.manty.co.kr/bbs/detail/develop?id=61

- https://velog.io/@max9106/Java-EnumMap