해시테이블은 해시함수를 사용하여 키를 해시값으로 매핑하고, 이 해시값을 색인(인덱스) 또는 주소삼아 데이터를 key와 함께 저장하는 자료구조이다. 단순하게 key
-value
로 이루어진 자료구조라고 생각하면 된다.
Hash, Hash Function
key를 해시함수라는 함수에 Input으로 넣어서 Ouput으로 나오는 것이 Hash(해시)
라고 생각하면 되고, 이 Hash(해시)가 저장위치 즉, 배열의 index(인덱스)
로 변환한다고 생각하면 된다.
Hash Function
는 key를 hash(해시)로 만들어내는 함수이다.
출처 : 위키백과-해시테이블
해시충돌
이라고 부른다. 해시 충돌 발생확률이 적을 수록 좋다.버킷
, 슬롯
이라고 한다.해시는 색인 또는 인덱스, hash function은 key->hash로 만들어 주는 함수, 해시테이블은 hash를 주소로 삼아 데이터를 저장하는 자료구조이다.
장점
단점
주요용도
만약 A,B 두가지 key가 있다고 하자. A와 B를 hash function으로 해시 값을 얻었는데 hash값이 2로 똑같이 나왔다. 이런 현상을 해쉬 출동
이라고 한다.
해시 함수로 해시를 만드는 과정에서 서로 다른 key가 같은 해시로 변경되면 같은 공간에 2개의 value가 저장되므로 key-value가 1:1로 매핑되어야 하는 해시 테이블의 특성에 위배된다.
통계적으로 해시 테이블의 공간 사용률이 70% ~ 80%정도가 되면 해시의 충돌이 빈번하게 발생하여 성능이 저하되기 시작한다고 한다.
출처: https://mangkyu.tistory.com/102 [MangKyu's Diary]
해쉬충돌의 문제점을 정리하자면,
1) Hashing을 해서 삽입하려 했으나 이미 다른 원소가 자리를 차지하고 있는 상황
2) 충돌은 해싱의 검색 속도를 떨어뜨리게 하여 버킷의 크기를 넘어 저장하게 되어 오버플로우가 발생할 수 있다
1) Separate Chaining 기법(폐쇄형) : 각 인덱스에 할당된 것이 값이 아니라 키와 값을 가진 LinkedList(연결리스트)로 추가적인 공간을 활용하는 것
Sandra가 들어가는데 충돌이 일어나니 기존에 있던 John의 값에 연결시켰다.
체이닝(Chaining)은 자료 저장 시, 저장소(bucket)에서 충돌이 일어나면 해당 값을 기존 값과 연결시키는 기법이다.
위의 사진에서 Sandra를 저장할 때 충돌이 일어났고, 기존에 있던 John에 연결시켰다. 이 때 연결리스트(Linked List) 자료구조를 이용한다. 해쉬충돌이 일어날 때 다음에 저장된 자료를 기존의 자료 다음에 위치시키는 것
이다.
Separate Chaining 장단점
장점 :
1) 한정된 저장소(Bucket)을 효율적으로 사용할 수 있다.
2) 해시 함수(Hash Function)을 선택하는 중요성이 상대적으로 적다.
3) 상대적으로 적은 메모리를 사용한다. 미리 공간을 잡아 놓을 필요가 없다.
단점 :
1) 한 Hash에 자료들이 계속 연결된다면(쏠림 현상) 검색 효율이 낮아질 수 있다.
2) 외부 저장 공간을 사용한다.
3) 외부 저장 공간 작업을 추가로 해야 한다.
2) Open Addressing 기법(개방형) : 충돌 발생시, 인접한 다른 비어있는 해시 버킷을 찾아 삽입하는 방법
Open Addressing(개방주소법)에서의 해시테이블은 1개의 해시와 1개의 값(value)가 매칭되어 있는 형태로 유지된다.
위의 그림을 보면, Sandra가 저장될때 해시가 John으로 채워져 있어서 그 다음 Hash에 Sandra를 저장했다. 그리고 Ted의 해시도 Sandra가 저장되어 있으므로 그 다음 해시에 Ted를 저장했다. 이처럼 비어있는 해시를 찾아 저장하는 방법을 Open Addressing라고 한다.
이 때, 비어있는 해시(Hash)를 찾는 과정은 동일해야 한다.(일정한 규칙을 따라 찾아가야 한다.)
i: index
고정폭
으로 이동 : 다음 해시(+1)나 i개(+i)를 건너뛰어 비어있는 버킷에 데이터를 저장한다.단, 군집현상이 일어나기 쉬워!제곱수
로 이동 : 충돌이 일어난 해시에서 제곱(+i^2)나 ((i+1)^2)을 한 비어있는 곳의 버킷에 데이터를 저장한다. 자세한 내용은 이블로그에서. 이 블로그
Open Addressing의 장단점
장점 :
1) 또 다른 저장공간 없이 해시테이블 내에서 데이터 저장 및 처리가 가능하다.
2) 또 다른 저장공간에서의 추가적인 작업이 없다.
단점 :
1) 해시 함수(Hash Function)의 성능에 전체 해시테이블의 성능이 좌지우지된다.
2) 데이터의 길이가 늘어나면 그에 해당하는 저장소를 마련해 두어야 한다.
HashTable이란 JDK 1.0부터 있던 Java의 API이고, HashMap은 Java2에서 처음 선보인 Java Collections Framework에 속한 API이다.
HashTable 또한 Map 인터페이스를 구현하고 있기때문에 HashMap과 HashTable이 제공하는 기능은 같다.
HashMap의 경우 동기화를 지원하지 않는다.
그래서 Hash Table은 동기화 처리라는 비용때문에 HashMap에 비해 더 느리다고 한다. 프로그래밍상의 편의성 때문에 멀티쓰레드 환경에서도 HashTable을 쓰기보다는 HashMap을 다시 감싸서 Map m == Collections.syschronizedMap(new HashpMap());
과 같은 형태가 더 선호된다.먼저 key값을 hashcode()라는 메소드를 통해 int형의 해쉬값으로 바꾸고 이를 버킷의 사이즈인 M으로 나눈 나머지가 해시 버킷의 진짜 인덱스가 된다.
int index = hashcode() % M;
HashMap에서 사용하는 충돌기법 방식은 Seaparate Chaining 이다. Open Addressing은 데이터를 삭제할 때 처리가 효율적이기 어려운데 HashMap에서 remove()메서드는 비번하게 호출될 수 있기 때문이다.
게다가 HashMap에 저자된 key-value 쌍 개수가 일정 개수 이상으로 많아지면, 일반적으로 Open Addressing은 Separate Chaining보다 느리다. Open Addressing은 버킷의 밀도가 높아질수록 Worst Case 빈도가 높아지지만, Chaining은 해시 출동이 잘 발생하지 않도록 조정하면 Worst Case를 줄일 수 있다.
자바 8부터는 Seperate Chaining에서 데이터 개수가 많아지면 LinkedList대신 Tree(red black tree
)를 사용해 성능적으로 더 좋아지게 하였다.
즉, 처음에는 해시 버킷을 LinkedList로 하고 8개 이상의 키-값 쌍이 모이면 LinkedList(O(N)
)를 Tree(O(logN)
)구조로 바꾼다. 만약 데이터가 삭제되어 버킷이 6개에 이르게 되면 다시 LinkedList 구조로 변경한다.
HashMap은 key-value 쌍 데이터 개수가 일정 개수 이상이 되면, 해시 버킷의 수를 두배로 늘린다. 해시 버킷의 수를 늘려서 해시 충돌의 확률을 줄이는 것이다.
O(1)
O(N)
O(logN)