我一直在阅读this article,因为它是由Jon Skeet on this answer链接的。我试图真正理解哈希是如何工作的以及为什么乔恩喜欢他提供的算法。我还没有声称对此有答案,但我确实对System.String
GetHashCode
的基本<<<<<==========
实施提出了具体问题。
考虑代码,重点关注带注释的public override unsafe int GetHashCode()
{
if (HashHelpers.s_UseRandomizedStringHashing)
return string.InternalMarvin32HashString(this, this.Length, 0L);
fixed (char* chPtr = this)
{
int num1 = 352654597;
int num2 = num1;
int* numPtr = (int*) chPtr;
int length = this.Length;
while (length > 2)
{
num1 = (num1 << 5) + num1 + (num1 >> 27) ^ *numPtr;
num2 = (num2 << 5) + num2 + (num2 >> 27) ^ numPtr[1];
numPtr += 2;
length -= 4; <<<<<==========
}
if (length > 0)
num1 = (num1 << 5) + num1 + (num1 >> 27) ^ *numPtr;
return num1 + num2 * 1566083941;
}
}
行:
{{1}}
为什么他们只处理每四个字符?而且,如果你愿意,他们为什么要从右到左处理呢?
答案 0 :(得分:5)
为什么他们只处理每四个字符?而且,如果你愿意,他们为什么要从右到左处理呢?
他们也没做。他们将字符作为整数值对处理(请注意,他们在while循环中使用*numPtr
和numPtr[1]
)。两个Int32
值占用4个字符的相同空间,这就是为什么它们每次从长度中减去4个。
这是从前到后处理(按数组顺序),但长度递减,因为它表示要处理的字符串剩余的长度。这意味着它们在可能的情况下一次以“4个字符的块”从左到右进行处理。
答案 1 :(得分:4)
它不会处理每四个字符。这是因为这一行:
int* numPtr = (int*) chPtr;
它将指针类型更改为int*
,这使得它每次使用char
时都会处理两个numPtr
。并且因为它在每次循环迭代时都使用了两次:
num1 = (num1 << 5) + num1 + (num1 >> 27) ^ *numPtr;
num2 = (num2 << 5) + num2 + (num2 >> 27) ^ numPtr[1];
每次需要4个字符。
答案 2 :(得分:3)
numPtr
是指向 32位整数的指针
循环的每次迭代都处理两个32位整数(*numPtr
和numPtr[1]
)。
因此,它以numPtr += 2
(跳过2个32位块)和length -= 4
结束(我们刚刚完成了4个16位char
)。