关于c#:我可以依赖GetHashCode()的值来保持一致吗?

关于c#:我可以依赖GetHashCode()的值来保持一致吗?

Can I depend on the values of GetHashCode() to be consistent?

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

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


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

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

The behavior of GetHashCode is dependent on its implementation, which might change from one version of the common language runtime to another. A reason why this might happen is to improve the performance of GetHashCode.


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    /// <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));
        }
    }

实现取决于框架的版本,但也取决于体系结构。即使在框架的x86和x64版本中,string.GetHashCode()的实现也不同。


What we did notice however, when an
object is in a hashed collection
object (a hashtable, a dictionary
etc), when 2 objects are not unique
but their hashcodes are, the hashcode
is only used as a first option lookup,
if there are non-unique hash codes
being used, the equality operator is
always used as a fall back to
detirmine equality.

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

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

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

我的理解正确吗?


这不是乔纳斯回答得很好的直接答案,但是如果您担心哈希中的相等性测试,这可能会有所帮助

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

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

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

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

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

但是,我们注意到的是,当一个对象位于哈希收集对象(哈希表,字典等)中时,如果两个对象不是唯一的但它们的哈希码相同,则该哈希码仅用作第一个选项查找,如果

总是使用非唯一的哈希码,等式运算符始终用作确定等式的后备。

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

更新

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

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

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

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

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

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

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

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


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


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

我一直对使用GetHashCode()感到厌倦,对我来说,简单地扮演自己的哈希算法可能是个好主意。好吧,至少由于这个原因,我最终写了一个快速的重新索引.aspx页。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /// <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,然后将nd复制并将其粘贴到新目录中,则哈希码会变得不同,即使是同一文件也是如此。显然,它是相同的.net版本,所有内容都相同。唯一改变的是路径。


推荐阅读

    linux服务端退出命令?

    linux服务端退出命令?,档案,命令,环境,异常,标准,网络,模式,终端,编辑,文件,l

    linux中启动服务命令?

    linux中启动服务命令?,服务,系统,命令,信息,工作,设备,网络,标准,名称,密码,l

    linux服务器常用命令?

    linux服务器常用命令?,工作,系统,地址,信息,命令,目录,管理,标准,设备,功能,

    linux筛选服务命令?

    linux筛选服务命令?,服务,系统,状态,软件,环境,主体,技术,号码,发行,名称,查

    linux命令的返回值?

    linux命令的返回值?,系统,工具,位置,管理,暂停,状态,项目,环境,数据,基础,lin

    linux服务器保存命令?

    linux服务器保存命令?,时间,状态,档案,电脑,命令,信息,位置,编辑,文件,模式,L

    linux服务器扫盘命令?

    linux服务器扫盘命令?,地址,工作,命令,目录,数据,单位,名称,系统,管理,信息,L

    linux命令切换服务器?

    linux命令切换服务器?,地址,名称,系统,环境,实时,命令,服务器,脚本,路径,版

    linux命令查的是啥?

    linux命令查的是啥?,系统,时间,信息,状态,命令,工具,名称,标准,软件,情况,Lin

    linux服务器搭建命令?

    linux服务器搭建命令?,系统,服务,软件,地址,平台,在线,密码,工具,环境,百度,l

    服务器重启命令linux?

    服务器重启命令linux?,工作,标准,设备,服务,系统,名称,命令,百度,网络,密码,

    linux服务端常用命令?

    linux服务端常用命令?,工作,地址,系统,网络,基础,命令,标准,工具,信息,管理,l

    linux禁用服务命令行?

    linux禁用服务命令行?,服务,系统,软件,管理,工具,信息,状态,平台,连续,技术,l

    linux停服务常用命令?

    linux停服务常用命令?,地址,工作,系统,命令,服务,信息,标准,管理,代码,进程,l

    linux服务器删除命令?

    linux服务器删除命令?,系统,服务,管理,情况,命令,工作,互动,地址,软件,较大,l

    linux开启服务命令?

    linux开启服务命令?,服务,标准,设备,工作,网络,系统,密码,命令,服务器,终端,

    linux服务器负荷命令?

    linux服务器负荷命令?,信息,电脑,中科,环境,工具,系统,平均,检测,情况,状态,l

    linux系统依赖命令?

    linux系统依赖命令?,系统,工作,地址,信息,管理,命令,软件,分析,基础,目录,lin

    linux常见服务器命令?

    linux常见服务器命令?,信息,工作,标准,地址,设备,系统,情况,时间,状态,命令,

    linux服务器基础命令?

    linux服务器基础命令?,设备,标准,工作,地址,服务,系统,信息,情况,图片,基础,