본문 바로가기
카테고리 없음

자바의 HashMap 심층분석 (2)

by 피크인사이트 2024. 2. 13.
반응형

지난 글에서는 자바의 중요한 자료구조 중 하나로, 데이터를 빠르고 효율적으로 관리하고 검색할 수 있는 HashMap의 개념과 작동 방법에 대해 살펴보았습니다.
이번 글에서는 HashMap 사용 시 발생할 수 있는 문제들과 문제해결을 위한 몇 가지 해결방안을 알아보겠습니다.

HashMap
<자바의 HashMap 심층분석 2>

 


1. 해시 충돌 문제

해시 충돌 문제는 지난 글에서도 언급된 바와 같이 해시 함수를 통해 생성된 해시 코드가 중복되는 현상을 말합니다.

즉, 서로 다른 두 개 이상의 키(KEY)가 동일한 해시코드를 가지는 경우를 의미합니다.

이런 상황이 발생하면, 서로 다른 키에 대한 값이 동일한 위치에 저장되어야 하는 문제가 발생합니다.

가장 대표적인 두 가지 해결 방법으로는 '개방주소법(Open Addressing)'과 '체이닝(Chaining)'입니다.


1) 개방 주소법 (Open Addressing)

개방주소법은 해시 충돌이 발생하면 그 다음 빈 공간을 찾아 데이터를 저장하는 방식입니다.

이 방식에는 선형 탐사, 이차 탐사, 더블 해싱 등의 방법이 있는데, 가장 간단한 선형 탐사는 현재 위치에서 고정된 거리만큼 이동하여 빈 공간을 찾는 방법입니다.


2) 체이닝(Chaining)

체이닝은 해시 충돌이 발생한 경우, 그 위치에 연결 리스트를 만들어 충돌이 발생한 데이터를 저장하는 방식입니다.
예를 들어, '멜론', '바나나', '체리'라는 키를 가진 데이터를 저장한다고 가정할 경우, 만약 ' 멜론 '와 '바나나'가 동일한 해시 코드를 가진다면, HashMap은 내부적으로 해당 해시 코드의 위치에 연결 리스트를 생성합니다.

이 연결 리스트에는 ' 멜론 '와 '바나나'에 대한 키-값 쌍이 저장됩니다.

이후 '사과'라는 키로 검색을 실행하면, 먼저 ' 멜론 '의 해시 코드를 계산하여 해당 위치의 연결 리스트에 접근합니다.

그런 다음 연결 리스트를 순회하면서 ' 멜론 '라는 키를 가진 데이터를 찾습니다.

이 방식의 장점은 충돌이 발생해도 데이터를 유실하지 않고, 충돌이 적게 발생하는 경우 빠른 검색 성능을 유지할 수 있다는 점입니다.

하지만 해시 코드가 동일한 키가 많은 경우, 연결 리스트를 순회하는 데 시간이 오래 걸릴 수 있어 검색 성능이 저하될 수 있기 때문에 효율적인 해시 함수를 선택하는 것이 중요합니다.


2. 메모리 사용량 증가 문제

HashMap은 해시 테이블을 기반으로 하며, 충분한 용량을 확보하지 않을 경우 버킷의 크기가 작아져 해시 충돌이 자주 발생할 수 있기 때문에 메모리 사용량을 증가시킬 수 있습니다.
HashMap을 사용할 때 발생할 수 있는 메모리 사용량 증가 문제에 대한 해결 방안은 다음과 같습니다

 

1) 적절한 초기 용량과 로드 팩터 설정

HashMap은 초기 용량과 로드 팩터(LoadFactor)를 기반으로 동적으로 크기를 조정합니다.

초기 용량은 해시 테이블의 시작 크기를 결정하고, 로드 팩터는 해시 테이블이 얼마나 가득 차야 크기를 조정할지를 결정합니다.

적절한 초기 용량과 로드 팩터 (LoadFactor )를 선택하여 해시 충돌을 최소화하고 메모리 사용량을 최적화할 수 있습니다.

 

2) 좋은 해시 함수 선택

해시 함수는 키를 해시 코드로 변환하여 해시 테이블에 저장하는 데 사용됩니다.

좋은 해시 함수를 선택하면 해시 충돌을 최소화하고 메모리 사용량을 줄일 수 있습니다.

키를 골고루 분산시키는 효율적인 해시 함수를 선택하여 메모리 사용량을 최적화할 수 있습니다.

 

3) 메모리 관리

사용하지 않는 키-값 쌍을 제거하고 메모리를 최적화하는 것이 중요합니다.

HashMap은 불필요한 데이터를 제거하지 않고 메모리를 재할당하지 않을 수 있습니다.

따라서 사용하지 않는 키-값 쌍을 제거하고 적절한 시기에 해시 테이블의 크기를 조정하여 메모리를 최적화할 수 있습니다.

 

4) ConcurrentHashMap 사용

ConcurrentHashMap은 여러 스레드에서 동시에 안전하게 사용할 수 있습니다.

HashMap보다 메모리 사용량이 더 많을 수 있지만, 여러 스레드에서 안전하게 사용할 수 있는 장점이 있습니다.

 

5) WeakHashMap 또는 SoftHashMap 사용

WeakHashMap이나 SoftHashMap은 키에 대한 참조가 존재하지 않을 경우 해당 항목을 자동으로 제거하는 기능을 제공합니다.

이를 통해 메모리 누수를 방지하고 메모리 사용량을 최적화할 수 있습니다.


이러한 방안들을 고려하여 HashMap을 사용할 때 메모리 사용량을 최적화하고 성능을 향상시킬 수 있습니다.


3. 스레드 안전성

HashMap은 기본적으로 스레드 안전성을 보장하지 않습니다.
즉, 멀티 스레드 환경에서 여러개의 스레드가 동시에 HashMap에 접근하여 데이터를 읽고 쓰면, 데이터의 일관성이 깨질 수 있습니다.
이런 문제를 '동시성 문제'라고 부르며, 해결방안은 다음과 같습니다.


1) Synchronized HashMap 이용

Java에서는 Collections.synchronizedMap() 메서드를 사용하여 HashMap을 동기화된(synchronized) 맵으로 변경할 수 있습니다.

이렇게 하면 동시에 맵에 접근하는 모든 스레드는 내부적으로 락(lock) 메커니즘을 통해 동기화되므로, 한 번에 하나의 스레드만이 맵을 수정할 수 있게 됩니다.
하지만 모든 데이터에 대해 한 번에 하나의 스레드만 접근이 가능하므로, 병렬 처리 성능이 저하될 수 있습니다.

Map New_map = Collections.synchronizedMap(new HashMap());

2) ConcurrentHashMap 이용

Java에서는 ConcurrentHashMap이라는 별도의 자료구조를 제공합니다.

ConcurrentHashMap은 segment라는 여러 개의 락으로 나누어져 있어, 동일한 시점에 여러 스레드가 서로 다른 세그먼트에 접근하면 동시에 데이터를 수정할 수 있습니다.

따라서 Synchronized HashMap에 비해 더 높은 병렬 처리 성능을 제공합니다.

ConcurrentHashMap<String, String> map = newConcurrentHashMap <>();

 

이런 방식으로 동시성 문제를 해결할 수 있지만 추가적인 리소스를 사용하기 때문에 성능에 영향을 줄 수 있습니다.

따라서 사용 환경과 요구사항에 따라 적절한 동기화 방법을 선택하는 것이 중요합니다.


4. 순서 보장 여부

HashMap은 키-값 쌍을 저장할 때 특정한 순서를 보장하지 않는 자료구조입니다. 이는 해시 맵의 내부적인 동작 원리로, 키의 해시 코드에 의해 데이터가 저장되는 위치가 결정되기 때문입니다.

따라서 데이터의 입력 순서나 키의 순서에 따라 데이터가 저장되는 것이 아닙니다.


이러한 순서 보장을 위해 다음과 같은 해결방안이 있습니다.

 

1) LinkedHashMap

Java에서는 순서를 유지하는 HashMap의 변형인 LinkedHashMap을 제공합니다.

LinkedHashMap은 HashMap과 동일한 해시 테이블 메커니즘을 사용하면서 동시에 더블 연결 리스트를 유지하여 데이터의 삽입 순서를 기억합니다.

따라서 LinkedHashMap을 순회하면 데이터가 삽입된 순서대로 접근할 수 있습니다.

Map <String, String>linkedHashMap = new LinkedHashMap <>();

 

 

2) TreeMap

TreeMap은 키에 대해 정렬된 순서를 유지하는 자료구조입니다.
내부적으로 레드-블랙 트리라는 자가 균형 이진 탐색 트리를 사용하여 데이터를 저장하므로, 키의 순서대로 데이터를 저장하고 검색할 수 있습니다.
하지만 트리 구조를 유지하기 위한 추가적인 작업으로 인해 HashMap이나 LinkedHashMap에 비해 성능이 떨어질 수 있습니다.

Map <String, String>treeMap = new TreeMap <>();


이렇게 데이터의 순서를 보장하는 자료구조를 사용하면, 순서에 의존하는 작업을 수행할 때 편리합니다.

하지만 이런 방법은 추가적인 리소스 사용으로 성능에 영향을 줄 수 있기때문에  사용 환경과 요구사항에 따라 적절한 자료구조를 선택하여야 합니다.


맺음말

지금까지, 데이터를 효율적으로 관리하고 검색하는 데 중요한 도구인 자바의 HashMap에 대해 살펴보았습니다.

이를 통해 HashMap의 핵심 개념과 작동 원리, HashMap 사용 시 발생할 수 있는 문제점과 그 해결 방안을 함께 살펴보았습니다.

다소 부족한 부분이 있겠지만 모쪼록 프로그램의 성능 향상과 효율적인 데이터 구조 구축에 도움이 되었길 바랍니다.

반응형