应用场景:在使用memcached时,有些作为key的字符串中会出现中文,需要为该字符串生成Hash值,然后将该hash值作为缓存key。
不考虑使用Enyim.Caching.Memcached.SHA1KeyTransformer(详见memcached对中文key的支持),因为这会对所有key进行Hash计算,而我们只需对会出现中文的key进行Hash计算。
目前我们使用的方法是直接调用.NET类库的String.GetHashCode()方法。
想在博问中问一下有没有比String.GetHashCode()计算速度更快的方法。
ILSpy查看到的String.GetHasCode()的实现代码如下:
public unsafe override int GetHashCode() { if (HashHelpers.s_UseRandomizedStringHashing) { return string.InternalMarvin32HashString(this, this.Length, 0L); } IntPtr arg_25_0; IntPtr expr_1C = arg_25_0 = this; if (expr_1C != 0) { arg_25_0 = (IntPtr)((int)expr_1C + RuntimeHelpers.OffsetToStringData); } char* ptr = arg_25_0; int num = 5381; int num2 = num; char* ptr2 = ptr; int num3; while ((num3 = (int)(*(ushort*)ptr2)) != 0) { num = ((num << 5) + num ^ num3); num3 = (int)(*(ushort*)(ptr2 + (IntPtr)2 / 2)); if (num3 == 0) { break; } num2 = ((num2 << 5) + num2 ^ num3); ptr2 += (IntPtr)4 / 2; } return num + num2 * 1566083941; }
Enyim.Caching.Memcached.SHA1KeyTransformer中是这样实现的:
public override string Transform(string key) { var sh = new SHA1Managed(); byte[] data = sh.ComputeHash(Encoding.Unicode.GetBytes(key)) return Convert.ToBase64String(data, Base64FormattingOptions.None); }
不同字符串 也可能生成相同的hashcode,这就是hash碰撞。
Dictionary在key的hash值碰撞时,是通过链表来解决的。
取值的时候,先通过key的hash值找到链表。然后遍历链表,用构造函数传入的IEqualityComparer<TKey> 来找到Equals key的对象。
原生的GetHashCode方法主要考虑的是分布均匀,即使几个差不多的字符串,hash值也能均匀分布。修改算法可以提高hash值得计算速度,但是对于有规律的字符串,分布未必有原生的均匀。造成对hash表的查找性能降低,反而得不偿失。毕竟hash值基本上就是为hash表准备的。
谢谢!学习了!
另外,分享一篇文章:从头到尾彻底解析Hash表算法
为什么不用Guid.NewGuid();生成的值作为Key呢?这个更快更方便。不知道对否?
还有一个问题String.GetHashCode()会有重复值。。
字符串不长的话,重复的概率很小吧?
@dudu: 重复值概率很大了。不过无所谓。重复就重复,因为字符串相同,所以重复了也表示一个意思。
@imfunny: 字符串相同?
@dudu: 对字符串相同gethashcode肯定也相同,同理。如果hash值相同了,字符串即使新的,也指向到以前的字符串。这个貌似之前一直被人说为gethashcode的一个坑。
@imfunny: 这样用作缓存key还是有些问题
@dudu: 这个上面有过讨论。http://bbs.csdn.net/topics/320009239 第72楼的总结。还有一些验证重复的方法。
反正肯定会有重复,字符串祝贺大于了int的范围。
@imfunny: 嗯,int类型的限制
将中文转成BASE64的编码方式作为key使用,这样速度应该也是可以的,而且也不会导致重复。
不过具体的时间比较就得专门去测试一下了
上面大部分人都不懂什么叫Hash函数
貌似很不建议使用的就是String.GetHashCode()了吧,记得有哪里说过,不同版本的.net framework 生成的hashcode是不一样的,这是灾难性的事情啊。