반응형
자바에서 데이터를 Key-Value 형태로 저장할 때 가장 많이 사용하는 자료구조 중 하나가 HashMap과 Hashtable이다. 두 클래스 모두 해시 테이블을 기반으로 데이터를 저장하지만, 중요한 차이점이 있다. 이번 포스팅에서는 HashMap과 Hashtable의 차이를 상세히 알아보고, 언제 어떤 것을 사용해야 하는지 살펴보겠다.
1. 기본 개념
HashMap
- java.util.HashMap은 Map 인터페이스를 구현한 클래스로, Key-Value 쌍으로 데이터를 저장한다.
- null 키와 null 값을 허용한다.
- 동기화를 제공하지 않으므로 여러 개의 스레드가 동시에 접근하면 문제가 발생할 수 있다.
- LinkedHashMap과 TreeMap의 부모 클래스이다.
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.ConcurrentHashMap은 HashMap과 Hashtable의 장점을 결합한 자료구조이다.
- 내부적으로 일부 영역만 잠그는 방식(세그먼트 락)을 사용하여 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을 사용하는 것이 가장 좋은 선택이다.
반응형
'Programming > Java' 카테고리의 다른 글
JAVA equals()와 hashCode() 오버라이딩할 때 주의할 점 (1) | 2025.04.10 |
---|---|
JAVA ArrayList vs LinkedList 차이점과 선택 기준 (0) | 2025.04.10 |
JAVA 프로그램이 실행되는 흐름 (1) | 2025.04.09 |
Java를 활용한 SFTP 파일 다운로드: 가장 최근 변경된 파일 찾기 (1) | 2025.04.08 |
Java Collections Framework (JCF) 상세 설명 (0) | 2025.04.07 |