HashCode란?
객체의 HashCode란, 객체를 식별하는 객체 고유의 하나의 정수 값을 의미한다.
GetHashCode() 함수는 객체의 HashCode값을 계산하여 가져오는 함수로, 해시값은 기본적으로 객체의 일생동안 바뀌지 않는다. 기본 메서드는 object의 GetHashCode()를 사용하나, 클래스나 자료구조별로 재정의가 되어있을 수 있다.
object의 기본 해시 메소드는 RuntimeHelpers.GetHashCode()를 사용하여 해시값을 얻어온다.
int num = 10;
var numHash1 = num.GetHashCode();
var numHash2 = num.GetHashCode();
object obj = new object();
var objHash1 = obj.GetHashCode();
var objHash2 = obj.GetHashCode();
string str = "Hello, World!";
var strHash1 = str.GetHashCode();
var strHash2 = str.GetHashCode();
다음의 코드를 5회 출력해보았다.
1. int는 GetHashCode()가 value값 자체를 가리키도록 재정의 되어있다.
// The absolute value of the int contained.
public override int GetHashCode()
{
return m_value;
}
2. object의 GetHashCode()는 항상 동일하다. 프로그램을 재실행해도 같은 자료형을 가지는 객체 간에 생성자를 호출하는 순서가 동일하다면 같은 값을 가진다.
3. String은 GetHashCode()를 프로그램 내에서 처음 호출할 때 시드값을 정하고, 해당 시드값과 내부 계산 코드를 통해 HashCode를 얻어온다. String의 HashCode는 객체의 일생동안 같고, 프로그램을 재실행 할 때 마다 달라진다.
String의 GetHashCode() 결과값을 실험해보기 위해 다음과 같은 테스트 코드를 실행하였다.
int loop = 100000;
Dictionary<int, string> map = new Dictionary<int, string>();
List<string> errors = new List<string>();
for (int i = 0; i < loop; i++)
{
var tempStr = $"{i}";
var tempStrHashCode = tempStr.GetHashCode();
if (false == map.TryAdd(tempStrHashCode, tempStr))
{
errors.Add($"tempStr : {tempStr}, tempStrHashCode : {tempStrHashCode}, map[] : {map[tempStrHashCode]}");
}
}
if (errors.Any())
{
foreach (var item in errors)
{
Console.WriteLine(item);
}
}
else
{
Console.WriteLine("No Error");
}
Dictionary Container는 중복된 Key값을 가질 수 없다는 것을 이용한 간단한 테스트 코드이다.
Dictionary에 string의 hashcode로 추가를 시도했을 때, 키값이 이미 있는 값이어서 실패할 경우 errors에 에러 로그를 남기고, 모든 추가가 끝난 이후 errors에 로그가 있다면 출력하고 errors가 비어있다면 "No Error"를 출력한다.
5회의 실행 결과는 다음과 같다.
이 테스트 출력으로 얻은 결과는 다음과 같다.
- 모든 String의 HashCode값은 내부의 내용이 동일해도 프로그램 실행 시 마다 다른 값을 가진다.
- 서로 다른 내용의 String이어도 동일한 HashCode를 가지는 경우가 있을 수 있어 유니크하지 않다.
- 동일한 HashCode 값의 발생 빈도수나 발생 위치도 랜덤하다.
HashCode의 사용
int값으로의 비교 속도는 다른 object나 클래스와의 비교보다 훨씬 빠른 속도를 가지고 있다.
빠른 속도 및 HashCode를 이용한 객체 동일성 비교에 사용하는 것은 하나의 좋은 방법일 수 있으나, String의 경우 메모리상의 객체의 비교보다 문자열 데이터 값에 대해 HashCode값을 정하는 경향이 있고, 서로 다른 문자열이더라도 같은 HashCode값을 가지고 있는 경우가 존재하기 때문에 유니크한 Id라고 정의하기 위험하다.
HashCode만으로의 고유성에 의존하지 않고 상황에 따라 적절한 부분에서의 사용을 하는 것이 좋고, 필요하다면 GetHashCode()와 Equals()를 재정의하여 자신만의 빠른 비교 방법을 만드는 것도 좋은 방법이 될 수 있다고 생각한다.
'공부 > C#' 카테고리의 다른 글
[C#] EventHandler 예제 <ThunderboltEvent> (1) | 2023.11.17 |
---|---|
[C#] EventHandler (0) | 2023.11.16 |
[C#] Unix Timestamp (0) | 2023.11.06 |
[C#] Enum.IsDefined (0) | 2023.11.04 |