두 문자열을 모두 교환할 수 있는 경우 두 문자열이 있는 구조에 대해 GetHashCode를 어떻게 구현합니까?

StackOverflow https://stackoverflow.com/questions/70303

  •  09-06-2019
  •  | 
  •  

문제

C#에는 다음과 같은 구조가 있습니다.

public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}

유일한 규칙은 UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

이 구조에 대해 GetHashCode 함수를 재정의하는 방법은 무엇입니까?

도움이 되었습니까?

해결책

MSDN:

해시 함수에는 다음 속성이 있어야 합니다.

  • 두 객체가 동일한 것으로 비교되면, GetHashCode 각 객체에 대한 메서드는 동일한 값을 반환해야 합니다.그러나 두 객체가 동일하다고 비교되지 않는 경우 GetHashCode 두 개체에 대한 메서드는 서로 다른 값을 반환할 필요가 없습니다.
  • 그만큼 GetHashCode 객체의 메서드는 객체의 반환 값을 결정하는 객체 상태에 대한 수정이 없는 한 일관되게 동일한 해시 코드를 반환해야 합니다. Equals 방법.이는 현재 애플리케이션을 실행하는 경우에만 해당되며, 애플리케이션을 다시 실행하면 다른 해시 코드가 반환될 수 있습니다.
  • 최상의 성능을 위해서는 해시 함수가 모든 입력에 대해 무작위 분포를 생성해야 합니다.

올바른 방법을 고려하는 것은 다음과 같습니다.

return str1.GetHashCode() ^ str2.GetHashCode() 

^ 다른 교환 연산으로 대체 가능

다른 팁

보다 존 스키트의 답변 - 다음과 같은 이진 연산 ^ 좋지 않기 때문에 종종 충돌 해시가 생성됩니다!

public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

'^'를 사용하는 것보다 '+' 연산자를 사용하는 것이 더 나을 수 있습니다. 왜냐하면 ('AA', 'BB')와 ('BB', 'AA')가 명시적으로 동일하기를 명시적으로 원하지만 ( 'AA', 'AA') 및 ('BB', 'BB')는 동일합니다(또는 해당 문제에 대한 모든 동일한 쌍).

이 솔루션에서는 '가능한 한 빨리' 규칙을 완전히 준수하지 않습니다. 왜냐하면 null의 경우 알려진 상수를 즉시 반환하는 대신 빈 문자열에 대해 'GetHashCode()'를 수행하기 때문입니다. 그러나 명시적으로 측정하지 않더라도 기꺼이 할 것입니다. 많은 null을 기대하지 않는 한 걱정할 만큼 차이가 크지 않을 것이라고 추측할 위험이 있습니다.

  1. 일반적으로 클래스에 대한 해시 코드를 생성하는 간단한 방법은 해시 코드 생성에 참여할 수 있는 모든 데이터 필드를 XOR하는 것입니다(다른 사람이 지적한 대로 null을 확인하도록 주의).이는 또한 UserInfo("AA", "BB") 및 UserInfo("BB", "AA")의 해시 코드가 동일해야 하는 (인공적인?) 요구 사항을 충족합니다.

  2. 클래스 사용에 대해 가정할 수 있다면 해시 함수를 향상시킬 수 있습니다.예를 들어 str1과 str2가 동일한 경우가 일반적이라면 XOR은 좋은 선택이 아닐 수 있습니다.그러나 str1과 str2가 이름과 성을 나타내는 경우 XOR이 아마도 좋은 선택일 것입니다.

이것이 실제 사례가 아니라는 것은 분명하지만 다음 사항을 지적할 가치가 있습니다.- 이것은 아마도 구조체 사용의 좋지 않은 예일 것입니다.구조체는 일반적으로 값 의미 체계를 가져야 하지만 여기서는 그렇지 않은 것 같습니다.- 해시 코드를 생성하기 위해 setter와 함께 속성을 사용하는 것도 문제를 야기합니다.

간단한 일반적인 방법은 다음과 같습니다.

return string.Format("{0}/{1}", str1, str2).GetHashCode();

엄격한 성능 요구 사항이 없는 한 이것이 제가 생각할 수 있는 가장 쉬운 방법이며 복합 키가 필요할 때 이 방법을 자주 사용합니다.이는 다음을 처리합니다. null 경우에는 문제가 없으며 (일반적으로) 해시 충돌이 발생하지 않습니다.문자열에 '/'가 필요한 경우 예상하지 못한 다른 구분 기호를 선택하세요.

public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}

ReSharper가 제안하는 내용을 따르면 다음과 같습니다.

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397은 결과 변수가 오버플로되고 해시 비트가 어느 정도 혼합되도록 하기에 충분한 크기의 소수로, 해시 코드의 더 나은 배포를 제공합니다.그렇지 않으면 397에는 같은 크기의 다른 소수와 구별되는 특별한 것이 없습니다.

아, 그렇습니다. Gary Shutler가 지적했듯이:

return str1.GetHashCode() + str2.GetHashCode();

넘칠 수 있습니다.Artem이 제안한 대로 캐스팅을 시도하거나 unchecked 키워드로 명령문을 둘러쌀 수 있습니다.

return unchecked(str1.GetHashCode() + str2.GetHashCode());

이것을 시도해 보세요:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()

많은 가능성.예:

return str1.GetHashCode() ^ str1.GetHashCode()

아마도 str1.GetHashCode() + str2.GetHashCode()와 같은 것일까요?또는 (str1.GetHashCode() + str2.GetHashCode()) / 2?이렇게 하면 str1과 str2가 바뀌는지 여부에 관계없이 동일하게 됩니다....

정렬한 다음 연결합니다.

return ((str1.CompareTo(str2) < 1) ? str1 + str2 : str2 + str1)
    .GetHashCode();

GetHashCode의 결과는 다음과 같습니다.

  1. 최대한 빨리.
  2. 가능한 한 독특합니다.

이를 염두에 두고 다음과 같이 하겠습니다.

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

편집하다: null을 잊어버렸습니다.코드가 수정되었습니다.

너무 복잡하고 null 등을 잊어버립니다.이는 버킷팅과 같은 작업에 사용되므로 다음과 같은 작업을 수행할 수 있습니다.

if (null != str1) {
    return str1.GetHashCode();
}
if (null != str2) {
    return str2.GetHashCode();
}
//Not sure what you would put here, some constant value will do
return 0;

이는 str1이 비정상적으로 많은 비율의 인스턴스에서 일반적이지 않을 가능성이 높다고 가정하여 편향된 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top