Hash를 왜 써야 하고, HashSet의 내부 구현 모습을 간단히 살펴보면서, 어떤식으로 이루어져 있는지 확인 해 보자.
https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98
해시 함수(hash function) 또는 해시 알고리즘(hash algorithm) 또는 해시함수알고리즘(hash函數algorithm)은 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다.
public class ArrayListMain {
private static final int DATA_COUNT = 100_000;
private static final int MAX_RANDOM = 10000000;
private static final int FIND_VALUE = 10000001;
public static void main(String[] args) {
// ArrayList 테스트
List<Integer> arrayList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < DATA_COUNT; i++) {
int inputData = random.nextInt(MAX_RANDOM);
if (!arrayList.contains(inputData)) arrayList.add(inputData);
}
arrayList.add(DATA_COUNT/2, FIND_VALUE);
//시간복잡도 O(n)
int findIndex = arrayList.indexOf(FIND_VALUE);
}
}
indexOf
를 통해서, Value 값을 찾을때 시간복잡도는 O(n) 이 걸리게 된다.ArrayList
에서는 동적으로 배열을 할당 하기 위해, 내부적으로 배열의 사이즈가 부족할 경우, 50% ~ 200%
증가시켜서 할당한다.List 통해, 데이터의 검색을 구현할 경우, 성능적으로 이슈가 걸림을 확인 할 수 있다.
하지만, 이렇게 된다면, 10000
이라는 데이터를 추가하고 싶다면, arrayList가 10000
까지 큰 범위로 할당되어있어야 한다.
따라서 이때, 공간이 많이 낭비된다.
step1 을 진행하였다면, 1
과 10000
이라는 데이터를 저장하기 위해서는, arrayList가 10000
의 사이즈의 배열로 확장되어있어야 한다.
만약 10000
을 다른 숫자로 치환할 수 있으면 어떻게 될까?
이번에는 1
, 2
, 5
, 8
, 14
, 99
라는 Values를 저장 시켜 보겠다.
단, Values 그대로 Index로 사용하지 않고, 모듈러 연산으로 나머지를 구해, 인덱스로 활용하겠다.
Java의 HashSet은 모듈러 연산 시, 배열의 크기로 연산 하지만 (Default 16), 설명을 위해서 10으로 진행한다
1
2
5
8
4
9
연산으로 나온 값을, Index로 활용하여, 다음과 같이 저장한다.
이때, 연산으로 나온 값을 HashIndex 라고 한다.
이제, 99
의 데이터를 넣기 위해 arrayList의 사이즈를 99
까지 할당하지 않아도 되서 메모리 사용에 굉장히 효율적이게 되었다.
step2
를 거쳤다면, 한가지 문제점이 발생한다. (...사실 아직 문제점이 많다)9
와 99
를 저장하게 되면, 같은 arrayList[9]
에다가 값을 저장한다는 것이다.9
를 먼저 저장해두고, 99
를 저장하게 되면, step2
의 상황상 둘 중 하나의 데이터는 소실되게 된다.이렇게 된다면, 9를 찾을때, arrayList[9] 값 안의 배열의 2번째 값을 찾을 수 있을 것이다. 이때 시간 복잡도는, O(1) 이 걸리지만, 최악의 조건에서는 O(n)이 걸릴 수 있다.
왜? O(n)이 걸릴 수 있나요?
저장하는 데이터가 모두, Index 9에 할당되게 되면, EX) 9, 19, 29, 39, 49...
모든 데이터는 결국 arrayList[9]에서 Full Scan으로 확인해야 되기 때문에 그렇다.
단, 평균적으로 그렇게 저장될 일을 많이 없기 때문에, 신경쓰지 않아도 된다
다음으로는, HashSet의 일부 기능들을 구현해보며, 기본 Object에서 제공하는 hashCode()와 equals() 메서드의 중요성, 문자열 해싱에 대해서 알아보겠다.