为什么字符串哈希代码在.NET中每次执行时都会发生变化



考虑以下代码:

Console.WriteLine("Hello, World!".GetHashCode());

首次运行:

139068974

第二次运行:

-263623806

现在考虑Kotlin中写的相同内容:

println("Hello, World!".hashCode())

首次运行:

1498789909

第二次运行:

1498789909

为什么string的哈希代码在.NET中每次执行时都会发生变化,而在JVM等其他运行时却不会发生变化?

为什么每次在.NET中执行字符串的哈希代码都会发生变化

简而言之,就是防止散列冲突攻击。您可以从<UseRandomizedStringHashAlgorithm>配置元素的文档中大致找到原因:

哈希表中的字符串查找通常是O(1(运算。但是,当发生大量冲突时,查找可能会变成O(n²(操作。您可以使用配置元素为每个应用程序域生成一个随机哈希算法,这反过来又限制了潜在冲突的数量,特别是当计算哈希代码的密钥基于用户输入的数据时。

,但在JVM等其他运行时上没有

不完全是这样,例如Python的哈希函数是随机的。当未启用<UseRandomizedStringHashAlgorithm>时,C#还在.net框架、核心1.0和核心2.0中生成身份散列。

对于Java来说,这可能是一个历史问题,因为算术是公开的,而且不好,请阅读本文。

为什么在.NET中每次执行都会更改哈希代码

因为在每次运行时更改字符串(和其他对象!(的哈希代码对开发人员来说是一个非常强烈的提示,即哈希代码在生成哈希的过程之外没有任何意义。

具体来说,文件上写着:

此外,.NET不保证GetHashCode方法的默认实现,此方法返回的值可能因.NET实现(如不同版本的.NET Framework和.NET Core(和平台(如32位和64位平台(而异。出于这些原因,请勿将此方法的默认实现用作唯一对象标识符以进行哈希处理。由此产生两个后果:

  • 您不应该假设相等的哈希代码意味着对象相等
  • 您永远不应该在创建哈希代码的应用程序域之外保留或使用哈希代码,因为同一对象可能会在应用程序域、进程和平台之间进行哈希

通过将给定对象的哈希代码从一次运行更改为下一次运行,运行时告诉开发人员不要将哈希代码用于任何跨越进程/应用程序域边界的内容。这将有助于将开发人员与标准类使用的GetHashCode算法的更改所产生的错误隔离开来。

让哈希代码从一次运行更改到下一次运行也不鼓励将哈希代码持久化以用作";这件事改变了吗;捷径。这既防止了错误对底层算法的更改,也防止了错误假设具有相同哈希码的两个相同类型的对象相等,而没有这样的保证(事实上,对于任何需要或允许超过32位的数据结构,由于鸽子洞原理,都不能做出这样的保证(。

为什么其他语言会生成稳定的哈希码

如果没有一个全面的逐个语言的审查,我只能推测,但主要原因可能是以下几种因素的结合:

  • 历史惯性(读作:"向后兼容性"(
  • 在定义语言规范时,没有充分理解稳定散列码的缺点
  • 在定义语言规范时,向哈希代码添加不稳定性在计算上过于昂贵
  • 哈希代码对开发人员来说不太显眼

相关内容

最新更新