본문 바로가기

Programming/Java

JAVA HashMap vs Hashtable vs ConcurrentHashMap 차이점

반응형

자바에서 데이터를 Key-Value 형태로 저장할 때 가장 많이 사용하는 자료구조 중 하나가 HashMapHashtable이다. 두 클래스 모두 해시 테이블을 기반으로 데이터를 저장하지만, 중요한 차이점이 있다. 이번 포스팅에서는 HashMapHashtable의 차이를 상세히 알아보고, 언제 어떤 것을 사용해야 하는지 살펴보겠다.

1. 기본 개념

HashMap

  • java.util.HashMapMap 인터페이스를 구현한 클래스로, Key-Value 쌍으로 데이터를 저장한다.
  • null 키와 null 값을 허용한다.
  • 동기화를 제공하지 않으므로 여러 개의 스레드가 동시에 접근하면 문제가 발생할 수 있다.
  • LinkedHashMapTreeMap의 부모 클래스이다.

HashMap 사용 예제

Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three", 3);
System.out.println(hashMap.get("two")); // 출력: 2

여러 스레드가 동시에 접근할 때 발생하는 문제(멀티스레드 환경)

Map<String, Integer> hashMap = new HashMap<>();
Runnable task = () -> {
    for (int i = 0; i < 1000; i++) {
        hashMap.put("key" + i, i);
    }
};
new Thread(task).start();
new Thread(task).start();
System.out.println(hashMap.size()); // 예상보다 작은 값이 나올 수 있음 (데이터가 덮어씌워지거나 유실됨)

 

Hashtable

  • java.util.Hashtable 역시 Map 인터페이스를 구현한 클래스로, HashMap과 유사한 방식으로 동작한다.
  • null 키와 null 값을 허용하지 않는다.
  • 모든 메서드에 synchronized가 적용되어 있어 여러 스레드가 동시에 접근해도 안전하다.
  • 하지만, 모든 메서드가 동기화되기 때문에 불필요한 락이 걸릴 수 있어 성능이 저하될 수 있다.

Hashtable 사용 예제

Map<String, Integer> hashtable = new Hashtable<>();
hashtable.put("one", 1);
hashtable.put("two", 2);
hashtable.put("three", 3);
System.out.println(hashtable.get("two")); // 출력: 2

동기화의 영향

Map<String, Integer> hashtable = new Hashtable<>();
Runnable task = () -> {
    for (int i = 0; i < 1000; i++) {
        hashtable.put("key" + i, i);
    }
};
new Thread(task).start();
new Thread(task).start();
System.out.println(hashtable.size()); // 2000 (데이터는 안전하게 저장되지만, 불필요한 락으로 성능 저하 발생 가능)

 

ConcurrentHashMap

  • java.util.concurrent.ConcurrentHashMapHashMapHashtable의 장점을 결합한 자료구조이다.
  • 내부적으로 일부 영역만 잠그는 방식(세그먼트 락)을 사용하여 Hashtable보다 성능이 뛰어나다.
  • null 키와 null 값을 허용하지 않는다.
  • 여러 스레드가 동시에 접근해도 안전하면서도 성능을 최대한 보장한다.

ConcurrentHashMap 사용 예제

Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put("one", 1);
concurrentHashMap.put("two", 2);
concurrentHashMap.put("three", 3);
System.out.println(concurrentHashMap.get("two")); // 출력: 2

멀티스레드 환경에서 안전한 예제

Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
Runnable task = () -> {
    for (int i = 0; i < 1000; i++) {
        concurrentHashMap.put("key" + i, i);
    }
};
new Thread(task).start();
new Thread(task).start();
System.out.println(concurrentHashMap.size()); // 2000 (데이터가 안전하게 저장되면서도 불필요한 락이 없어 성능 유지)

 

2. 주요 차이점 비교

비교 항목 HashMap Hashtable ConcurrentHashMap
동기화 여부 지원하지 않음 모든 메서드 동기화 부분 동기화 (세그먼트 락)
null 키 허용 여부 가능 불가능 불가능
null 값 허용 여부 가능 불가능 불가능
성능 빠름 상대적으로 느림 Hashtable보다 빠름
여러 스레드가 접근할 때 데이터 손실 위험 있음 안전하지만 불필요한 락으로 인해 느림 안전하면서도 빠름
추천 사용 환경 단일 스레드 레거시 코드 유지보수 멀티스레드 환경

 

3. 성능 비교

HashMap은 동기화를 제공하지 않기 때문에 Hashtable보다 성능이 뛰어나다. 멀티스레드 환경이 아니라면 HashMap을 사용하는 것이 일반적으로 더 효율적이다. 반면, Hashtable은 모든 메서드가 synchronized로 보호되므로 동시 접근 시에도 안전하지만, 동기화 비용 때문에 성능이 저하될 수 있다.

멀티스레드 환경에서 동기화된 Map이 필요하다면, Hashtable보다는 ConcurrentHashMap을 사용하는 것이 더욱 효율적이다. ConcurrentHashMap은 부분적인 동기화를 제공하여 성능과 스레드 안전성을 모두 만족시킨다.

 

4. 언제 사용해야 할까?

사용 상황 추천 컬렉션
단일 스레드 환경에서 빠른 데이터 저장/검색이 필요할 때 HashMap
멀티스레드 환경에서 스레드 안전한 Map이 필요할 때 ConcurrentHashMap
레거시 코드 유지보수가 필요할 때 Hashtable (하지만 가급적 변경을 고려)

 

5. 결론

  • HashMap은 빠르고 효율적이지만, 스레드가 안전하지 않다.
  • Hashtable은 스레드가 안전하지만, 성능이 떨어지고 null을 허용하지 않는다.
  • ConcurrentHashMap은 멀티스레드 환경에서도 높은 성능을 유지하며 동기화 문제를 효율적으로 해결한다.
  • 멀티스레드 환경에서는 ConcurrentHashMap을 사용하는 것이 가장 좋은 선택이다.
반응형