【问题标题】:GetHashCode() with string keys带有字符串键的 GetHashCode()
【发布时间】:2011-03-20 05:55:29
【问题描述】:

大家好,我一直在阅读为 .NET 中的对象实现 GetHashCode() 覆盖的最佳方法,我遇到的大多数答案都涉及以某种方式将数字类型的成员中的数字组合在一起以提出一个方法。问题是,我有一个使用字母数字字符串作为键的对象,我想知道仅使用字符串作为键的对象的内部 ID 是否存在根本性错误,如下所示?


// Override GetHashCode() to return a permanent, unique identifier for
// this object.
static private int m_next_hash_id = 1;
private int m_hash_code = 0;
public override int GetHashCode() {
  if (this.m_hash_code == 0)
    this.m_hash_code = <type>.m_next_hash_id++;
  return this.m_hash_code;
}

有没有更好的方法来为使用字母数字字符串作为键的对象提供唯一的哈希码? (不,字母数字字符串的数字部分不是唯一的;其中一些字符串实际上根本没有数字。)任何想法都将不胜感激!

【问题讨论】:

    标签: c# .net c#-2.0 gethashcode


    【解决方案1】:

    我相信您通常希望 GetHashCode() 返回一些通过它的值而不是实例来标识对象的东西,如果我在这里理解这个想法,我认为您的方法将确保 GetHashCode() 在两个不同的对象上使用等效值会返回不同的哈希值,因为它们是不同的实例。

    GetHashCode() 旨在返回一个值,让您可以比较两个对象的值,而不是它们的引用。

    【讨论】:

      【解决方案2】:

      这不是为对象生成哈希的好模式。

      理解 GetHashCode() 的目的很重要 - 它是一种生成对象标识属性的数字表示的方法。哈希码用于允许对象作为字典中的键,并且在某些情况下可以加速复杂类型之间的比较。

      如果您只是生成一个随机值并将其称为哈希码,那么您就没有可重复性。具有相同键字段的另一个实例将具有不同的哈希码,并且会违反 HashSet、Dictionary 等类所期望的行为。

      如果您的对象中已经有一个识别字符串成员,只需返回其哈希码。

      documentation on MSDN for implementers of GetHashCode() 对于计划重写该方法的任何人来说都是必读的:

      实施者须知

      哈希函数 用于快速生成数字 (哈希码)对应于 一个对象的价值。哈希函数是 通常特定于每种类型,并且,对于 唯一性,必须至少使用以下之一 实例字段作为输入。

      哈希函数必须有 以下属性:

      如果两个对象比较相等,则 每个对象的 GetHashCode 方法 必须返回相同的值。然而, 如果两个对象不比较为 相等,GetHashCode 方法为 两个对象不必返回 不同的值。

      对象的 GetHashCode 方法 必须始终返回相同的哈希 代码只要没有 修改对象状态 确定返回值 对象的 Equals 方法。请注意,这 仅对当前执行为真 的应用程序,并且 可以返回不同的哈希码,如果 应用程序再次运行。

      为了获得最佳性能,哈希 函数必须生成一个随机 所有输入的分布。

      例如,执行 提供的GetHashCode方法 字符串类返回相同的哈希 相同字符串值的代码。 因此,两个 String 对象返回 如果它们代表相同的哈希码 相同的字符串值。此外,该 方法使用中的所有字符 合理随机生成的字符串 分布式输出,即使当 输入聚集在特定范围内 (例如,许多用户可能有 仅包含较低的字符串 128 个 ASCII 字符,即使一个 字符串可以包含 65,535 个中的任何一个 Unicode 字符)。

      【讨论】:

        【解决方案3】:

        哈希码不必是唯一的。如果您的 Equals 实现是正确的,则可以为两个实例返回相同的哈希码。 m_next_hash_id 逻辑被破坏了,因为它允许两个对象具有不同的哈希码,即使它们比较相等。

        MSDN 提供了一套关于如何实现EqualsGetHashCode 的很好的说明。 Several of the examples here 根据对象字段的哈希码实现 GetHashCode

        【讨论】:

          【解决方案4】:

          是的,更好的方法是使用您已有的字符串的哈希码。如果字母数字字符串定义了您拥有的对象的身份,它的哈希码将非常适合您的对象的哈希码。

          增加一个静态字段并将其用作哈希码的想法是一个糟糕的想法。哈希码应该在可能值的空间中均匀分布。除其他外,这可确保它在用作哈希表中的键时表现良好。

          【讨论】:

            【解决方案5】:

            您可以对您在对象中使用的非数字值调用GetHashCode()

            private string m_foo;
            public override int GetHashCode()
            {
                return m_foo.GetHashCode();
            }
            

            【讨论】:

            • 但是如果那个字符串改变了呢?例如,我可以创建一个新的用户对象: User foo = new User();并且构造函数设置 User.Id = ""。稍后,如果我说 User.Id = "A12345";而我返回this.Id.GetHashCode()作为foo.GetHashCode()的结果,会不会改变了,违反了对象的哈希码永远不应该改变的原则?
            • @King - 有几种不同的方法可以使用哈希码。给定相同的起始值,哈希码的值需要始终相同。如果您的值是可变的,则需要存储生成的哈希码并在调用 GetHashCode() 时返回它。
            • @Hans - 这值得商榷,实际上取决于对象的用途。例如,如果您在哈希表中使用此对象,则如果值更改,则哈希码不会更改。但是,相同的起始值必须始终产生相同的哈希码。
            • @King Skippus:哈希码不应该改变的原因是它们有助于识别一个对象。但如果该对象的字段发生变化,它就不是同一个对象。它可能是相同的实例,但不再是相同的数据。您不会期望 Equals() 为具有不同键字段的两个不同实例返回 true 吗?那么为什么要试图阻止哈希值改变呢?您当然可以存储哈希,但如果您这样做的原因是将可变类型作为键存储在字典中,那么正确的解决方案是避免这种情况。
            • @JonB 这不是真的,尽管它包含真理。如果一个对象是哈希表中的一个键,那么这个对象一定不能改变。如果是这样,它将根据其先前的散列位于表的内部,因此无论散列是否保持不变,都不会找到它(除非偶然机会根据表的大小而减少)。对象不能改变意味着哈希不能改变,但重要的是前者而不是后者。
            猜你喜欢
            • 2014-09-10
            • 1970-01-01
            • 1970-01-01
            • 2016-08-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-01-24
            相关资源
            最近更新 更多