본문 바로가기

C#/Dictionary

사용자가 정의한 클래스를 키로 사용하는 딕셔너리

사용자가 정의한 클래스Equals 메서드GetHashCode 메서드오버라이드 한다.

 

 

문자열이나 숫자값이 아닌 사용자가 작성한 클래스를 딕셔너리의 키로 사용하고 싶을 때가 있을 것이다.

 

월과 일을 멤버로 갖고 있는 MonthDay라는 클래스를 정의했을 때 이 MonthDay객체를 키로 이용하는 다음 코드를 작성했다.

//MonthDay 객체를 키로 이용하고 각 날짜에 대응하는 휴일을 저장한다.
var dict = new Dictionary<MonthDay, string>
{
    { new MonthDay(6, 6), "현충일" },
    { new MonthDay(8, 15), "광복절" },
    { new MonthDay(10, 3), "개천절" },
}

var monthDay = new MonthDay(8, 15);
var selected = dict[monthDay];    //KeyNotFoundException 예외 발생
Console.WriteLine(selected);

 

그러나 위 코드를 실행하면 KeyNotFoundException 예외가 발생한다.

 

제대로 동작시키려면 사용자가 정의한 Month 클래스에서 Equals 메서드GetHashCode 메서드오버라이드 해야한다.

 

아래와 같이 클래스 정의 부분에 함수를 오버라이드 해주면 MonthDay 클래스를 딕셔너리의 키로 이용할 수 있다.

class MonthDay
{
    public int Day { get; private set; }
    public int Month { get; private set; }
    public MonthDay(int month, int day)
    {
        this.Month = month;
        this.Day = day;
    }
 
    //MonthDay끼리 비교한다
    public override bool Equals(object obj)
    {
        var other = obj as MonthDay;
        if (other == null)
            throw new ArgumentException();
        return this.Day == other.Day && this.Month == other.Month; }

        //해시코드를 구한다
        public override int GetHashCode()
        {
            return Month.GetHashCode() * 31 + Day.GetHashCode();
        }
}

 

해시 코드(해시값)는 객체의 값을 가지고 일정한 계산을 해서 구한 int 형 값을 말하여

딕셔너리 내부에서는 값을 찾을 때 인덱스로 이용한다.

 

같은 객체로부터는 항상 같은 해시값이 생성되어야 한다.

 

그리고 이 해시값을 사용해 본래의 객체를 복원할 수는 없다.

 

 

다른 객체가 동일한 해시값을 생성해도 문제가 없다.

그러나 동일한 값이 반환되는 빈도가 높으면 딕셔너리가 가진 빠르다는 장점을 잃게 된다.

다른 객체의 해시값은 최대한 다른 값이 반환되도록 구현해야 한다.

 

위 코드에 나온 GetHashCode 메서드에서 31이라는 소수곱하는 것은 해시값이 서로 다르게 퍼지게 한다는 의미가 있다.

31이라는 숫자는 이렇게 해시값을 만들 때 많이 사용하는 소수이다.

 

해시값이 같은 경우에는 Equals 메서드를 통해 객체가 같은지 여부가 판단된다.

(그래서 Equals 메서드도 오버라이드 해야 한다.)