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

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

有帮助吗?

解决方案

如果我没记错的话,GetHashCode 在给定相同值的情况下是一致的,但不能保证在不同版本的框架之间保持一致。

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

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

其他提示

我遇到了类似的问题,我用依赖于 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));
        }
    }

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

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

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

您的桌面运行的是 Win2008 x86 吗?因为Win2008包含版本 2.0.50727.1434, ,它是 Vista RTM 中包含的 2.0 的更新版本。

但是,当对象位于哈希集合对象中(凸面,词典等)中时,我们确实注意到的是,当两个对象不是唯一的,但是它们的hashcodes是唯一的,则仅将哈希码用作第一个选项查找,如果有非非选项查找,则 - 唯一的哈希代码被使用,平等运算符始终用作倒退以归档。

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

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

如果您的散列实现实现了良好的分布,则不需要此搜索,即每个桶一个项目。

我的理解正确吗?

不是对您问题的直接回答,乔纳斯已经很好地回答了,但是如果您担心哈希中的相等性测试,这可能会有所帮助

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

我们需要重载 equals 运算符,因此需要重载对象的 GetHashCode 函数,因为它们变得易失性和无状态,并且直接从数据中获取自身,因此在应用程序的一个位置,我们需要确保可以查看对象等于另一个对象 如果它来自相同的数据, ,不仅仅是如果它是相同的引用。我们唯一的数据标识符是Guid。

等于运算符很容易满足,因为我们只需检查记录的 Guid(在检查 null 后)。

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

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

然而,我们确实注意到,当一个对象位于散列集合对象(散列表、字典等)中时,当两个对象不唯一但它们的散列码是唯一时,散列码仅用作第一个选项查找(如果不存在) - 使用唯一的哈希码, 相等运算符始终用作确定相等的后备.

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

更新

为了演示,我们有一个哈希表:

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

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

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

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

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

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

当我使用对象 D 的键调用对象的哈希表时,经过 1 步(哈希查找)后将返回对象 D1

    /// <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;
    }

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

我不得不说……你不能依赖它。例如,如果我通过 c# 的 md5 哈希代码运行 file1 并将相同的文件复制并粘贴到新目录...即使是同一个文件,哈希代码也会有所不同。显然它是相同的.net版本,一切都相同。唯一改变的是路径。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top