我可以依赖GetHashCode()的值来保持一致吗?

时间:2008-09-09 22:52:33

标签: c# hash

假设使用相同的字符串值,GetHashCode()的返回值是否保证一致? (C#/ ASP.NET)

我今天将我的代码上传到了服务器,令我惊讶的是我不得不重新索引一些数据,因为我的服务器(win2008 64位)与台式机相比返回了不同的值。

9 个答案:

答案 0 :(得分:31)

如果我没有弄错,GetHashCode在给定相同值的情况下是一致的,但不保证在不同版本的框架中保持一致。

来自String.GetHashCode()的MSDN文档:

  

GetHashCode的行为取决于其实现,该实现可能从公共语言运行库的一个版本更改为另一个版本。可能发生这种情况的原因是为了提高GetHashCode的性能。

答案 1 :(得分:11)

我有一个类似的问题,我在数据库表中填充了依赖于String.GetHashCode的信息(不是最好的主意),当我升级服务器时,我正在使用x64,我注意到我从String获取的值.GetHashCode与表中已有的不一致。我的解决方案是使用我自己的GetHashCode版本,它在x86框架上返回与String.GetHashCode相同的值。

这是代码,不要忘记使用“允许不安全的代码”进行编译:

    /// <summary>
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks.
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static unsafe int GetHashCode32(string s)
    {
        fixed (char* str = s.ToCharArray())
        {
            char* chPtr = str;
            int num = 0x15051505;
            int num2 = num;
            int* numPtr = (int*)chPtr;
            for (int i = s.Length; i > 0; i -= 4)
            {
                num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
                if (i <= 2)
                {
                    break;
                }
                num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
                numPtr += 2;
            }
            return (num + (num2 * 0x5d588b65));
        }
    }

答案 2 :(得分:5)

实现取决于框架的版本,但也取决于architecture。 string.GetHashCode()的实现在框架的x86和x64版本中是不同的,即使它们具有相同的版本号。

答案 3 :(得分:0)

我想知道32位和64位操作系统之间是否存在差异,因为我确定我的服务器和家用计算机都运行相同版本的.NET

我总是厌倦使用GetHashCode(),对我来说,简单地使用自己的哈希算法可能是一个好主意。好吧,至少我最终写了一个快速的重新索引.aspx页面因为它。

答案 4 :(得分:0)

您是否正在运行Win2008 x86作为桌面?因为Win2008包含版本2.0.50727.1434,这是Vista RTM中包含的2.0的更新版本。

答案 5 :(得分:0)

不能直接回答你的问题,Jonas已经回答得很好,但是如果你担心哈希中的平等测试,这可能会有所帮助

根据我们的测试,根据您对哈希码的要求,在C#中,哈希码对于Equality操作不需要是唯一的。例如,请考虑以下事项:

我们需要重载equals运算符,因此我们的对象的GetHashCode函数变得不稳定和无状态,并直接从数据中获取,所以在应用程序的一个地方我们需要确保一个对象将被视为与另一个对象相等,如果它来自相同的数据,而不仅仅是它是相同的引用。我们唯一的数据标识符是Guids。

equals运算符很容易满足,因为我们刚检查了记录的Guid(在检查为null之后)。

不幸的是,HashCode数据大小(作为int)取决于操作系统,而在我们的32位系统上,哈希码将是32位。在数学上,当我们重写GetHashCode函数时,不可能从大于32位的guid生成一个唯一的哈希码(从反过来来看,如何将32位整数转换为guid?)。 p>

然后我们做了一些测试,我们将Guid作为一个字符串并返回Guid的HashCode,它几乎总是在我们的测试中返回一个唯一的标识符,但并非总是如此。

然而,我们注意到,当一个对象在散列集合对象(散列表,字典等)中时,当2个对象不是唯一但是它们的散列码是,则散列码仅用作第一个选项查找,如果有一些非唯一的哈希码被使用,等于运算符总是被用来作为detirmine相等的后退

正如我所说,这可能与您的情况有关,也可能与您的情况无关,但如果它是一个方便的提示。

<强>更新

为了演示,我们有一个Hashtable:

键:对象A(哈希码1),值对象A1

密钥:对象B(哈希码1),值对象B1

密钥:对象C(哈希码1),值对象C1

键:对象D(哈希码2),值对象D1

密钥:对象E(哈希码3),值对象E1

当我使用对象A的键调用对象的哈希表时,对象A1将在2步后返回,调用哈希码1,然后对密钥对象进行等式检查,因为没有唯一的键哈希码1

当我用对象D的键调用对象的哈希表时,对象D1将在1步后返回,哈希查找

答案 6 :(得分:0)

  

然而,当我们注意到时,我们注意到了什么   对象位于哈希集合中   对象(哈希表,字典   等),当2个对象不唯一时   但他们的哈希码是哈希码   仅用作第一个选项查找,   如果有非唯一的哈希码   正在使用,等于运算符   总是用来作为回归   detirmine平等。

这是散列查找的工作方式,对吧?每个存储桶都包含具有相同哈希码的项目列表。

因此,要在这些条件下找到正确的项目,需要使用值相等比较进行线性搜索。

如果您的哈希实现实现了良好的分发,则不需要进行此搜索,即每个桶一个项目。

我的理解是否正确?

答案 7 :(得分:0)

    /// <summary>
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a)
    /// </summary>
    private static int GetFNV1aHashCode(string str)
    {
        if (str == null)
            return 0;
        var length = str.Length;
        // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707"
        int hash = length;
        for (int i = 0; i != length; ++i)
            hash = (hash ^ str[i]) * 16777619;
        return hash;
    }

此实现可能比之前发布的不安全实现慢。但更简单,更安全。

答案 8 :(得分:-1)

我不得不说......你不能依赖它。例如,如果我通过c#的md5哈希码运行file1并复制nd将同一个文件粘贴到一个新目录...哈希代码变得与众不同甚至很难它是同一个文件。显然它是相同的.net版本,同样的一切。唯一改变的是路径。