【问题标题】:GetHashCode doesn't guarantee uniqueness of value/number it returnsGetHashCode 不保证它返回的值/数字的唯一性
【发布时间】:2012-09-30 05:28:37
【问题描述】:

我正在开发一个简单的 2D 环境并绘制每个对象,例如line, rectangle and ... 通过调用 GetHashCode() 获得一个唯一的 id

现在,我注意到 MSDN page 并不能保证其结果是唯一的:

GetHashCode 方法的默认实现不保证不同对象的唯一返回值。此外,.NET Framework 不保证 GetHashCode 方法的默认实现,它返回的值在不同版本的 .NET Framework 之间是相同的。因此,此方法的默认实现不得用作散列目的的唯一对象标识符。

现在,问题是除了GetHashCode() 方法之外还存在哪些其他选项?

谢谢, 阿米特

【问题讨论】:

标签: c# .net


【解决方案1】:

也许最好完全摆脱哈希码? GetHashCode 非常适合快速轻松地修复,但如果您需要对象的真实 ID,那么您应该创建真实 ID。像 32/64 位自动递增整数之类的东西可能就足够了。

虽然哈希码的冲突率与哈希的长度相关,但仍不能保证在发生冲突之前达到可能的最大唯一哈希数。如果您自己管理 ID,则可以提前计划有足够的可用 ID。

另外 - 您对不同版本框架的 GetHashCode() 的评论。我只能想象,如果您将哈希保存到某种保存文件中,然后尝试重新加载它们,却发现它们与正在运行的程序的哈希不匹配,因为它们被保存了,这对您的情况很重要通过不同版本的框架。如果是这种情况,我会更建议您自己创建和管理对象的 ID。

【讨论】:

    【解决方案2】:

    您需要生成自己的唯一 ID

    如果您的对象具有自然键,则有时可以从对象属性中派生唯一 ID。
    如果对象没有自然键,则必须生成唯一 ID,您通常会在构造函数中将唯一 ID 传递给对象。

    GetHashCode 的唯一 ID 很差,因为它不能保证是唯一的。
    在内部,.NET 不使用 GetHashCode 来实现唯一性。
    .NET 在内部使用 GetHashCode 来加速相等比较和 HashBuckets。

    如果您要生成自己的唯一 ID,则应覆盖 GetHashCode 和 Equals。
    这样 .NET 可以使用您的唯一标识符进行相等比较。

    .NET GetHashCode() 不是必需的,也不保证是唯一的。
    .NET GetHashCode() 不仅限于 Int32。
    .NET GetHashCode() 是 Int32。

    如果 GetHashCode 不相等,则两个对象不相等。
    如果 GetHashCode 相等,则两个对象可能相等也可能不相等。 Equals 是决胜局。
    对于速度,首先比较 GetHashCode。 GetHashCode 也用于 hashbuckets 以提高 HashSet 和 Dictionary 等集合的速度。

    如果一个哈希是唯一的,那么它被认为是一个完美的哈希。

    经典例子

    class Point: object 
    {
       protected int x, y;
    
       public Point(int xValue, int yValue)
       {
            x = xValue;
            y = yValue;
       }
       public override bool Equals(Object obj) 
       {
          // Check for null values and compare run-time types.
          if (obj == null || GetType() != obj.GetType()) 
             return false;
    
          Point p = (Point)obj;
          return (x == p.x) && (y == p.y);
       }
       public override int GetHashCode() 
       {
          return x ^ y;
       }
    }
    

    既然 Point 有 Int32 X Int32 可能的值,那么显然它不能用单个 Int32 唯一标识。 GetHashCode 仍然是有价值的并且是必需的。只有 1/Int32 的机会需要更昂贵的 Equals 并且 GetHashCode 用于哈希桶。

    考虑简单点

    class Point: object 
    {
       protected byte x, y;
    
       public Point(byte xValue, byte yValue)
       {
            x = xValue;
            y = yValue;
       }
       public override bool Equals(Object obj) 
       {
          // Check for null values and compare run-time types.
          if (obj == null || GetType() != obj.GetType()) 
             return false;
    
          Point p = (Point)obj;
          return (x == p.x) && (y == p.y);
       }
       public override int GetHashCode() 
       {
          return (x * 256) + y;
       }
    }
    

    在这个简单点中,GetHashCode 将唯一标识对象。 您不能覆盖其中之一。必须不覆盖或覆盖两者。

    【讨论】:

    • 你认为这会比一个简单的工厂设计模式更好吗?
    • @Origin 第二段“如果对象没有自然键,则必须生成唯一 ID”。这只是使用自然键的一个例子。如果对象具有自然键,请考虑 P1 = new Point(1,2) P2 = new Point(1,2)。你想让 P1.Equals(P2) 返回真还是假?如果自然密钥已经存在,则从自然密钥派生 ID 不会用完变量。如果自然键用于 GetHashCode,则不应更改这些值。同意工厂是最常见的模式。并且最适合大多数情况。
    【解决方案3】:

    这取决于您使用唯一 ID 的目的。听起来您正在使用来识别对象实例,这可能意味着哈希码不是您想要的。

    如果两个对象是彼此的 .Equals(),则它们应该具有相同的哈希码,但正如您所发现的,相反的情况并非如此(具有相同的哈希码并不意味着它们是 .Equals( ))。

    您需要唯一 ID 做什么?如果您不使用哈希码将对象放入查找中,则最好为它们分配一个唯一的 ID,如 Guid (var uniqueId = Guid.NewGuid())。

    【讨论】:

    • GUID 范围仍然有限,并且比 SHA-1 短。
    • 你是对的。我总是被这个名字愚弄。自动递增的 int 可能会更好。
    【解决方案4】:

    没有哈希函数保证返回值的唯一性。

    这取决于碰撞的可能性有多小。

    GetHashCode() 返回一个 32 位整数,这可能不足以假设唯一性。 考虑其他算法如 SHA-1、SHA-2,哈希长度较长,碰撞概率远低于 32 位整数。

    【讨论】:

    • +1 用于指出散列不保证返回唯一值,但 -1 用于建议具有相同问题的其他散列例程。散列是这里唯一 ID 的错误答案。
    • @Will:我不同意。给定足够长的散列,您几乎可以将散列视为输入的唯一标识符。采用 256 位 SHA-2 哈希(长度仍然相当适中):如果您在接下来的 140 亿年中每秒生成 10^9 个哈希,那么其中发生 一个 冲突的概率约为 10^- 20.这意味着对于所有实际应用来说,发生这种情况的机会为零。毕竟人们已经考虑到总是存在错误的可能性(例如,由于辐射,...),这比哈希冲突更有可能。
    • @Grizzly 差不多?覆盖等于您自己更改。
    • @Blam:你在做什么?
    • @Grizzly 当你可以使用 4 字节标识符(甚至可能是 1或 2 个字节)具有 的冲突概率?零比无穷小好。
    猜你喜欢
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    相关资源
    最近更新 更多