给出以下密钥:
int key = Guid.NewGuid().GetHashCode();
此密钥是否与Guid的唯一性一致?
答案 0 :(得分:43)
pigeonhole principle说不。 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)操作
答案 1 :(得分:12)
GetHashCode()
返回一个整数 - 它不能像Guid
那样唯一,所以不 - 可能会发生冲突,并且无法保证唯一性。
散列码的要点是它应该在散列范围内均匀分布,这样碰撞通常很少见,但你总是有碰撞的机会,并且必须适应这种情况。 / p>
答案 2 :(得分:12)
就在今天,我已经注意到Guid.GetHashCode()
的另一个问题:在Microsoft .NET实现中,而不是每个"字节"对Guid
进行了散列:Guid
中有6个字节没有被散列,因此对其中一个字节的任何更改都不会更改散列码。
我们可以在reference source:
中看到它return _a ^ (((int)_b << 16) | (int)(ushort)_c) ^ (((int)_f << 24) | _k);
因此_d
,_e
,_g
,_h
,_i
,_j
字节不会被散列。这对&#34;顺序&#34;有重要影响。 Guid
s,如:
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
不会被哈希。
答案 3 :(得分:4)
我在另一个答案中确切地the problem xanatos describes了。我有一个类,其中两个Guid
值用于区分不同的对象,我发现我得到了可怕数量的碰撞(我的Guids不是随机生成的)。这是我用来解决问题的代码。 Guid1
和Guid2
是区分对象的Guid
类型的属性。代码遵循the approach described by Jon Skeet here。
public override int GetHashCode()
{
int hash = 173;
foreach (Byte b in Guid1.ToByteArray().Concat(Guid2.ToByteArray()))
{
hash = hash * 983 + b;
}
return hash;
}
答案 4 :(得分:3)
Guid是128位数字。 int是32位数字,因此它不能像Guid那样“独特”。
此外,GetHashCode返回...一个哈希码,它并不意味着任何方式都是唯一的。请参阅此处的其他讨论,了解为何GetHashCode()存在。