Guid和GetHashCode的唯一性
鉴于以下关键:
int key = Guid.NewGuid().GetHashCode();
这个钥匙作为Guid的独特性是独一无二的吗?
鸽笼原则说没有。 GUID有16个字节的信息 – 128位。 int
有32位信息。 (编辑:为了澄清由于注释,.NET GUID将允许这些128位任意设置,据我所知;随机生成的GUID遵循更严格的模式,因此没有2 128个不同的值,这将是随机的但仍然超过2 32。 )
有2 128个可能的GUID和2 32个可能的哈希码 – 因此您不可能为每个GUID使用不同的哈希码。
不仅如此 – GetHashCode()
绝不代表唯一性。 如果它可以 ,那么这很好 – 但它没有必要,即使有足够的int
值可用。
int.GetHashCode()
返回(比方说)除以2的值是完全有效的 ……所以-1,0和1都将得到哈希码0; 3和4将得到2的哈希码等。它不会好(并且它会比返回值慢) – 但它将是一个有效的实现。 它将满足GetHashCode
所有约束 – 即如果你在两个相等的值上调用它,它将返回相同的哈希码。
事实上,为所有值返回一个常量是一个有效的实现 – 尽管它是一个相当无用的实现,因为它将哈希表的正常快速查找呈现为O(N)操作。
GetHashCode()
返回一个整数 – 它不能像Guid
一样唯一,所以不 – 可能存在冲突,并且不保证唯一性。
哈希码的重点是它应该在哈希范围内均匀分布,这样冲突通常很少,但你总是有碰撞的机会,并且必须适应这种情况。
就在今天,我已经注意到Guid.GetHashCode()
另一个问题:在Microsoft .NET实现中,并非Guid
每个“字节”都经过哈希处理: Guid
的6个字节没有经过哈希处理,因此任何更改都是其中一个不会改变哈希码。
我们可以在参考源中看到它:
return _a ^ (((int)_b << 16) | (int)(ushort)_c) ^ (((int)_f << 24) | _k);
所以_d
, _e
, _g
, _h
, _i
, _j
字节不进行哈希处理。 这对“顺序” Guid
有重要影响,如:
c482fbe1-9f16-4ae9-a05c-383478ec9d13 c482fbe1-9f16-4ae9-a05c-383478ec9d14 c482fbe1-9f16-4ae9-a05c-383478ec9d15 ... c482fbe1-9f16-4ae9-a05c-383478ec9dff c482fbe1-9f16-4ae9-a05c-383478ec9e00 c482fbe1-9f16-4ae9-a05c-383478ec9e01
与Guid
一样,生成的不同哈希值的数量非常少(256个不同的值),因为3478ec9d
/ 3478ec9e
不会被散列。
Guid是一个128位的数字。 int是32位数字,因此它不能像Guid那样“独特”。
此外,GetHashCode返回…一个哈希码,它并不意味着任何方式都是唯一的。 有关GetHashCode()存在的原因,请参阅此处的其他讨论。
我正好知道xanatos在另一个答案中描述的问题。 我有一个类,其中两个Guid
值用于区分不同的对象,我发现我得到了可怕数量的碰撞(我的Guids不是随机生成的)。 这是我用来解决问题的代码。 Guid1
和Guid2
是区分对象的Guid
类型的属性。 该代码遵循Jon Skeet在此描述的方法 。
public override int GetHashCode() { int hash = 173; foreach (Byte b in Guid1.ToByteArray().Concat(Guid2.ToByteArray())) { hash = hash * 983 + b; } return hash; }