HashMap은 Java에서 Map 인터페이스를 구현하고 있는 클래스 중 하나이다.
key:value 가 1:1 로 Mapping 되고 삽입, 삭제, 검색의 시간복잡도가 평균적으로 O(1) 을 보여주는 좋은 이점을 가지고 있다.
이런 좋은 성능 때문에 자주 사용하는데 동시성 문제가 고려되지 않기 때문에
→ Threadsafe 하지 못함 → Multi-Thread에서 동기화처리 필요 !
실무에서는 ConcurrentHashMap, AtomicLong 을 사용한다고 한다.
면접에서도 자주 등장하는 HashMap 의 자료구조 및 동작원리 와 ConcurrentHashMap 에 대해서도 정리해보자.
HashMap의 내부 구조는 배열(array) 로 되어 있으며 해싱(hashing)을 통하여 데이터가 저장될 인덱스를 구한다.
Key, Value 형식의 데이터를 저장하는 자료구조로 큰 맥락에서는 동일하지만
세가지 차이점을 보인다.
- NULL 허용 여부
- 동기화 여부
- Enumuration 여부
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
public V get(Object key) {}
public V put(K key, V value) {}
}
Java 의 HashMap 클래스를 보면 synchronized 키워드가 존재하지 않는다. 따라서 성능은 좋지만 Multi-Thread 환경에서 사용할 수 없다.
→ 신뢰성과 안정성이 떨어진다.
➕
NULL을 허용하지 않는다.
Enurmation을 제공하지 않는다.
→Fail-fast Iterator
public synchronized V get(Object key){
Entry<?,?> tab[] = table;
int hash = key.hashCode();
.
.
.
}
public synchronized V put(K key, V value) {
.
.
.
}
get(),put() 메서드를 확인해보면 synchronized 하게 구현 되어 있음을 확인 할 수 있다.
→ 동기화 처리가 되어 있다. → 데이터의 무결성 보장
➕
NULL을 허용한다.
not fail-fast Enumeration 을 제공한다.
Enumeration과 Iterator 의 가장 큰 차이점은 fail-fast 방식 개념이다.
콜렉션 클래스들은 저장된 객체들에 대한 순차적 접근을 제공한다.
Enumeration은 순차적 접근 시 콜렉션 객체에 변경이 일어나도 이를 무시하고, 끝까지 동작한다.
Iterator는 순차적 접근이 모두 끝나기 전에 콜렉션 객체에 변경이 일어날 경우 순차적 접근이 실패되면서 예외를 return하게 되는데 이를 fail-fast 방식이라고 부른다.
- HashMap의 Multi-Thread 환경에서의 동기화 문제를 보완했다
- HashTable 보다 좋은 성능을 가진다.
- key,value 에 NULL값을 허용하지 않는다.
- Fail-Safe Iterator를 지원한다.
HashTable은 동기화시 synchronized 키워드를 이용하여 메소드 전체에 락을 거는데에 반해 ConcurrentHashMap은 lock striping 을 이용하여 내부적으로 16개의 세그먼트를 두고 세그먼트마다 별도의 락을 건다.
- AtomicLong은 Long 자료형을 갖고 있는 Wrapping 클래스이다.
- Thread-safe로 구현되어 멀티쓰레드에서 synchronized 없이 사용할 수 있다.
- 또한 synchronized 보다 적은 비용으로 동시성을 보장할 수 있다.
Single-Thread 환경에서는 HashMap을 사용하고
Multi-Thread 환경에서는 HashTable ConcurrentHashMap 을 사용하자.
https://tomining.tistory.com/169
https://devlog-wjdrbs96.tistory.com/269
https://www.dineshonjava.com/difference-between-hashmap-and/