C# 为什么GetHashCode方法需要在C中执行shift#

C# 为什么GetHashCode方法需要在C中执行shift#,c#,gethashcode,C#,Gethashcode,根据MSDN GetHashCode方法: public struct Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public override bool Equals(Object obj) { if (!(obj is Point)) return

根据MSDN GetHashCode方法:

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (!(obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    { 
        return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
    } 

    private int ShiftAndWrap(int value, int positions)
    {
        positions = positions & 0x1F;

        // Save the existing bit pattern, but interpret it as an unsigned integer.
        uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
        // Preserve the bits to be discarded.
        uint wrapped = number >> (32 - positions);
        // Shift and wrap the discarded bits.
        return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
    }
}
公共结构点
{
私人INTX;
私营企业;
公共点(整数x,整数y)
{
这个.x=x;
这个。y=y;
}
公共覆盖布尔等于(对象对象对象)
{
如果(!(obj为点))返回false;
点p=(点)obj;
返回x==p.x&y==p.y;
}
公共覆盖int GetHashCode()
{ 
返回ShiftAndWrap(x.GetHashCode(),2)^y.GetHashCode();
} 
私有int ShiftAndWrap(int值、int位置)
{
位置=位置&0x1F;
//保存现有位模式,但将其解释为无符号整数。
uint number=BitConverter.ToUInt32(BitConverter.GetBytes(值),0);
//保留要丢弃的位。
uint wrapped=编号>>(32位);
//移位并包装丢弃的位。

返回BitConverter.ToInt32(BitConverter.GetBytes)((numberHashCode
的要点是创建一个分布,以便数据结构可以将数据分配到特定的存储桶中。这并不意味着相等

    /// <summary>
    /// Checks if this hashset contains the item
    /// </summary>
    /// <param name="item">item to check for containment</param>
    /// <returns>true if item contained; false if not</returns>
    public bool Contains(T item) {
        if (m_buckets != null) {
            int hashCode = InternalGetHashCode(item);
            // see note at "HashSet" level describing why "- 1" appears in for loop
            for (int i = m_buckets[hashCode % m_buckets.Length] - 1; i >= 0; i = m_slots[i].next) {
                if (m_slots[i].hashCode == hashCode && m_comparer.Equals(m_slots[i].value, item)) {
                    return true;
                }
            }
        }
        // either m_buckets is null or wasn't found
        return false;
    }
如果查看
HashSet
的内部结构,可以看到该类使用
HashCode
来标识正确的bucket,然后使用
Equals
方法来确定相等性

    /// <summary>
    /// Checks if this hashset contains the item
    /// </summary>
    /// <param name="item">item to check for containment</param>
    /// <returns>true if item contained; false if not</returns>
    public bool Contains(T item) {
        if (m_buckets != null) {
            int hashCode = InternalGetHashCode(item);
            // see note at "HashSet" level describing why "- 1" appears in for loop
            for (int i = m_buckets[hashCode % m_buckets.Length] - 1; i >= 0; i = m_slots[i].next) {
                if (m_slots[i].hashCode == hashCode && m_comparer.Equals(m_slots[i].value, item)) {
                    return true;
                }
            }
        }
        // either m_buckets is null or wasn't found
        return false;
    }
//
///检查此哈希集是否包含该项
/// 
///要检查容器的项目
///如果包含项,则为true;如果不包含项,则为false
公共布尔包含(T项){
if(m_bucket!=null){
int hashCode=InternalGetHashCode(项目);
//请参阅“HashSet”级别的注释,说明为什么“-1”出现在for循环中
对于(int i=m_bucket[hashCode%m_bucket.Length]-1;i>=0;i=m_slot[i]。下一步){
if(m_slots[i].hashCode==hashCode&&m_comparer.Equals(m_slots[i].value,item)){
返回true;
}
}
}
//m_bucket为空或未找到
返回false;
}

所以冲突是好的,它只是在那里,所以确保相对平等的分布,以允许更快的识别和检索。这意味着,在您的情况下,这两个点将放在同一个桶中,但它们的
Equals
方法将用于识别它们。

我不能说为什么它们选择这个特定的哈希值取消执行,但关于这个问题:

  • 为什么这个方法先做右移(32个位置),然后做左移位置,它有具体的含义吗
  • 这里的
    ShiftAndWrap()
    方法是一种算法的通用实现,该算法将一个值左移N位,并将溢出包装回末尾。因此,在执行移位之前,他们首先获取最左边的N位,然后将其附加到末尾

    下面是调用
    ShiftAndWrap()
    时的情况,如果我们只使用8位值(
    byte
    s)并使用
    value
    =(二进制)11010010和
    位置
    =3:

    value = 11010010
    
    positions = 3
    
    wrapped = value >> (8 - positions)
            = 11010010 >> (8 - 3) 
            = 11010010 >> 5 
            = 00000110
    
    result = value << positions | wrapped
           = 11010010 << 3 | 00000110 
           = 10010000 | 00000110 
           = 10010110
    
    我们可以看到,每个字符乘以不同的31次幂,因此
    stop
    的哈希代码与
    pots
    的哈希代码不同


    至于他们为什么选择
    2
    作为移动位置的数量,这可能是任意的,或者他们可能已经做了一些评估,以确定移动的程度可能会产生最佳分布。

    唯一的目的是尽量减少冲突我个人看不到复杂且难以理解的数学的必要性对于本例中的哈希代码计算。为了解决您的问题,为什么不使用msdn上使用元组的一种更简单的算法?如果哈希代码函数的结果不唯一,则此函数对哈希代码计算没有意义。@RanjitSingh是说,这真的可以最小化冲突?为什么?@MichaelWürthner确切地说,元组对象更容易,但是,当在哈希表中存储大量对象时,它可能会显著影响应用程序的整体性能@陳品翰 一种方法是做异或运算,忽略32位之后的位,我有人这样做,然后一个大于32位小于32位的值会发生冲突,但是如果我们包装这些值,那么冲突可以最小化,而不是丢弃该位。是的!我知道哈希代码只是让识别更快,但我不明白的是简单的GetHashCode方法是:public override int GetHashCode(){return x^y;}为什么它需要ShiftAndWrap?这真的可以最大限度地减少冲突??哦!我明白了!!你的解释很清楚。非常感谢!!