经常用hash,但从来没有自己实现过。因为我懒得自己造轮子,或者简单说:懒。
今天想到几个关于hash的问题,通过看源代码的方式解决了,发现自己要实现一个hash也很简单啊。
1)对于整型数据和字符串数据最常用的hash函数是啥?
答:其实就是整数求余,hashcode=key%size。如果key不是整型怎么办?瞎搞成整型呗。(另外即使一开始是整型,也可以瞎搞一下,maybe碰撞少些)float也是4B,直接看成int就好了。double?前32位和后32位异或,搞定。字符串?复杂一点,一种做法是:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],^是n次方的意思,不是异或,31也可以替换成大一些的其他素数(如37)。
ps:可以用Thomas Wang's 32 bit Mix Function可以对32bit的数据类型瞎搞一下,据说碰撞少些。
2)标准库一般用哪个?开链表法还是插后面法?
答:插后面。
3) hash表的冗余度一般多少,是不是达不到一定的冗余度就要扩容了?
答:java的hash实现默认冗余最少25%,也就是如果容量100,存第76个key的时候要扩容:new一个大一倍的数组,把原来所有数据搬过来。嗯。这个代价是很高。所以如果能知道最后的数据量,最好一开始就指定一个比较合适的size。
今天之所以对hash感兴趣,是突然想到不知道能不能在几十亿规模的key上运用hash。目前看来依靠标准库的实现是不行的:因为一个数组没那么大。java中数组最多有2g个元素(大概20亿),c++更少(经老猫的指点发现:64位程序可以malloc很大的数组,new不行,new总大小只能2g,不过我猜标准库中用new的可能性大些哈)。要自己实现,自己实现倒是不难。设计一下比较合理的hash函数就可以了。比如字符串的hashcode,可以用上式搞完后去除一个不大不小的素数得到double,然后直接把double看成64位整型。