2010-04-28 4 views
28

이 합성 GetHashCode()를 구현하는 가장 좋은 방법은 무엇입니까 :내가 간단한 클래스가

내가 해시 코드의 더 나은 분포를 얻을 것입니다 만약 내가 대신 무언가 같이했다면 내가 궁금
public class TileName { 
    int Zoom, X, Y; 

    public override bool Equals (object obj) 
    { 
     var o = obj as TileName; 
     return (o != null) && (o.Zoom == Zoom) && (o.X == X) && (o.Y == Y); 
    } 

    public override int GetHashCode() 
    { 
     return (Zoom + X + Y).GetHashCode(); 
    } 
} 

:

public override int GetHashCode() 
    { 
     return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode(); 
    } 

이 클래스는 사전 키로 사용되므로 괜찮은 배포인지 확인하고 싶습니다.

+5

약간의 경고 : 'Zoom','X' 및'Y '필드가 유형 생성 후 변경 될 수 없음을 확인하십시오. 인스턴스의 해시 코드가 변경되지 않아야합니다. 그렇지 않으면 해시에서 키를 찾을 수 없게됩니다 (FxCop이이를 확인한다고 생각합니다). 'int zoom, X, Y;'호출을'read, int, Zoom, X, Y;'로 변경하여 명확하게하십시오. – Steven

답변

55

Jon Skeet in this SO answer과 마찬가지로 소수를 선택하고 단일 해시 코드를 곱한 다음 모든 것을 합산하는 것이 가장 좋습니다. xor 해시와

public int GetHashCode() 
{ 
    unchecked 
    { 
     int hash = 17; 
     // Maybe nullity checks, if these are objects not primitives! 
     hash = hash * 23 + Zoom.GetHashCode(); 
     hash = hash * 23 + X.GetHashCode(); 
     hash = hash * 23 + Y.GetHashCode(); 
     return hash; 
    } 
} 

문제점은 다음과 같습니다 다음 해시 단지 확대 될 것 Y 같다

  • X 경우, 다음 X^Y = X^X = 0
  • xor이 대칭 연산자를 보유하고 있기 때문에, 그것은 생산합니다 객체에 대해 정확히 같은 해시입니다. [Zoom = 3, X = 5, Y = 7], [Zoom = 3, X = 7, Y = 5], [Zoom = 7, X = 5, Y = 3]

이러한 사실 때문에 xor-method가 충돌을 일으킬 가능성이 커집니다.

Jons 게시 외에도 명시 적으로 오버플로를 무시하기 위해 unchecked 컨텍스트를 사용하는 것이 좋습니다. MSDN 말한다 좋아하기 때문에 : 어느 checkedunchecked 사용 경우

이, 상수 표현식이 선택되어 컴파일 시간에 검사 기본 오버 플로우를 사용합니다. 그렇지 않은 경우 표현식이 비정상적인 경우 런타임 오버플로 검사는 컴파일러 옵션 및 환경 구성과 같은 다른 요소에 따라 달라집니다.

일반적으로 오버플로는 선택되지 않지만 일부 환경에서는 다소 실패하거나 일부 컴파일러 옵션으로 빌드되었을 수 있습니다. 그러나이 경우 오버 플로우를 명시 적으로 확인하지 않으려합니다.그런데

:

업데이트

someInt.GetHashCode() 반환 someInt. 이와 같이, 이것은 단일 충돌없이 가장 빠른 가능한 해시 분포입니다. 어떻게 int를 int-hash로 매핑 할 것인가? :) 내가 말하고 싶었던 그래서 : 귀하의 첫 번째 방법 :

return (Zoom + X + Y).GetHashCode(); 

하고 두 번째 :

return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode(); 

정확히 동일합니다. GetHashCode에 전화를 걸지 않아도 충돌이 발생할 가능성이 큽니다. 어쩌면 xor 메소드보다 더 나쁠 수도 있습니다. 세 int 모두에 대해 작은 정수 값이있을 가능성이 높습니다.

업데이트 2 :

내가 ChaosPandions에 대한 주석에 쓴 바와 같이 게시 : 당신은 그냥 그 세 INT 값 및 X가있는 경우 YZoom이 (1000 만 이하) 상대적으로 작은 숫자입니다

public int GetHashCode() 
{ 
    return (X << 16)^(Y << 8)^Zoom; 
} 

그냥 (가독성 빅 엔디안에서 예) 해시 값의 비트를 분산 :

,536,913,632 한도 좋은 해시 생성 할 수있다 10
00000000 00000000 00000011 00110001 X = 817 
00000000 00000000 00011011 11111010 Y = 7162 
00000000 00000000 00000010 10010110 Zoom = 662 

00000011 00110001 00000000 00000000 X << 16 
00000000 00011011 11111010 00000000 Y << 8 
00000000 00000000 00000010 10010110 Zoom 

00000011 00101010 11111000 10010110 (X << 16)^(Y << 8)^Zoom 
3
public override int GetHashCode() 
{ 
    return (Zoom.ToString() + "-" + X.ToString() + "-" + Y.ToString()).GetHashCode(); 
} 
+0

GetHashCode를 호출 할 때마다 적어도 하나의 새로운 문자열과 하나의 새로운 문자열 배열이 만들어지기 때문에 이것은 좋은 배포판을 제공하지만 실제로 성능에는 좋지 않습니다. 당신은 오히려 이것보다 나쁜 분배를 가지고 있습니다. – Steven

+0

@Steven,이 값은 계산 된 후에 캐시 될 수 있으며, Zoom, X 또는 Y가 설정 될 때마다 캐시 된 값을 지울 수 있습니다. – Fede

+0

@Fede : 느린 알고리즘의 결과를 캐시하거나 빠른 알고리즘을 사용할 수 있습니다. 그리고 btw : 캐싱은 읽기 전용 필드가 있거나 필드에 이전 값을 저장해야하는 경우에만 의미가 있습니다. 그러면 지저분해질 것입니다 ... –

5

저는 실제로 이것이 실제로 효과적이라고 생각했습니다.

public override int GetHashCode() 
{ 
    return Zoom.GetHashCode()^X.GetHashCode()^Y.GetHashCode(); 
} 
+0

이것이 문제의 구현보다 뛰어나지 만 여전히 큰 문제는 아닙니다. 예를 들어 필드 순서를 고려하지 않으므로'{Zoom = 1, X = 2, Y = 3}','{Zoom = 2, X = 3, Y = 1}','{Zoom = 3, X = 1, Y = 2}'등은 모두 동일한 해시를 반환합니다. 일종의 롤링 곱셈 및/또는 합계가이 문제를 피할 수 있습니다. – LukeH

+0

@ 루크 : 동의했다. @ChoasPandion : 여기를 읽어보십시오 : http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416 –

+0

@ 루크 - 나 동의합니다. 일반적으로 저는 항상 문제에 대한 가장 간단한 해결책을 사용하려고 노력할 것입니다. 심각한 응용 프로그램의 경우 충돌 가능성이 적은 알고리즘을 사용하는 것이 좋습니다. – ChaosPandion

7

귀하의 질문에 대한 구현이 이상적이지 않습니다.

public override int GetHashCode() 
{ 
    // 269 and 47 are primes 
    int hash = 269; 
    hash = (hash * 47) + Zoom.GetHashCode(); 
    hash = (hash * 47) + X.GetHashCode(); 
    hash = (hash * 47) + Y.GetHashCode(); 
    return hash; 
} 

가 (메모리에서, 내가 생각하는 C# 컴파일러가 비슷한 것을 사용 예를 들어, 그들은 { Zoom=3, X=1, Y=2 }

내가 보통이 같은 것을 사용

등 등 { Zoom=1, X=2, Y=3 } 정확히 같은 해시, { Zoom=2, X=3, Y=1 }를 돌아갑니다 익명 형식에 대해 GetHashCode 메서드를 생성 할 때)

+0

아, Jon Skeet 게시물도 읽었습니다. D –

+0

@Philip : Jon이 이전에 언급 한 것을 보았지만, 원래 내가 선택한 부분을 기억할 수 없습니다. 나는 이것이 꽤 일반적인 구현이라고 생각한다. – LukeH

+0

그래, 그 좋은 연습, 더 많은 사람들이 스스로 익숙해 져야합니다. –

관련 문제