最近在设计一个分布式的key-value系统的时候中,出于性能和存储空间的考虑,准备把string类型的key替换为它的HashCode值.
GetHashCode这个方法可能很多人都有所了解,不熟悉的可以看看这里:http://msdn.microsoft.com/zh-cn/library/system.object.gethashcode.aspx
以下信息只限于String.GetHashCode,其他的例如Object.GetHashCode根据其他对象的实现不同而不同:
1.对于不同的对象类型,或者同类型的不同值,返回值是可能重复的
2.String.GetHashCode的实现是平台相关的,32位版本和64位版本并不一样
3.String.GetHashCode的实现和.net版本有关,将来可能还会改变
4.同样的程序集,同样的平台,同样的字符串, 返回同样的HashCode
5.基于默认实现,虽然对象是会重复的,但是由于hashcode是int32类型并且实现的比较良好,那么只有当数据量达到或者接近2^32数量级的时候才需要考虑重复的情况
- 2^32次方大约在40多亿
- 假设有1千万数据,那么有一条或者多余一条数据重复的可能性约为400分之一 (这个概率对于一个要求较高的系统来说太危险了,而且现在很多系统的数据是远大于千万级别的)
6. .net4.0里面String.GetHashCode的实现代码如下
// Gets a hash code for this string. If strings A and B are such that A.Equals(B), then
// they will return the same hash code.
[System.Security.SecuritySafeCritical] // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override int GetHashCode() {
unsafe {
fixed (char *src = this) {
Contract.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'");
Contract.Assert( ((int)src)%4 == 0, "Managed string should start at 4 bytes boundary");
#if WIN32
int hash1 = (5381<<16) + 5381;
#else
int hash1 = 5381;
#endif
int hash2 = hash1;
#if WIN32
// 32bit machines.
int* pint = (int *)src;
int len = this.Length;
while(len > 0) {
hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
if( len <= 2) {
break;
}
hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
pint += 2;
len -= 4;
}
#else
int c;
char *s = src;
while ((c = s[0]) != 0) {
hash1 = ((hash1 << 5) + hash1) ^ c;
c = s[1];
if (c == 0)
break;
hash2 = ((hash2 << 5) + hash2) ^ c;
s += 2;
}
#endif
#if DEBUG
// We want to ensure we can change our hash function daily.
// This is perfectly fine as long as you don't persist the
// value from GetHashCode to disk or count on String A
// hashing before string B. Those are bugs in your code.
hash1 ^= ThisAssembly.DailyBuildNumber;
#endif
return hash1 + (hash2 * 1566083941);
}
}
}
考虑到GetHashCode依赖.net版本和平台,还有重复概率..还是放弃了用HashCode来代替Key的想法
最终选择使用md5 (16 bytes)