在查看hashtable源码的时候,我们会发现,hashtable的桶数全部使用的是质数,因为我们在hashtable的定义中,hash函数使用的是标准的求模函数,因此这样定义桶数有利于元素各个桶之间的均匀分布。
例子:
举一个有点极端的例子,假设我们的元素全是偶数1,4,6,8,10,12,14,1,6,18,20,22
如果我们使用4个桶:
0: 4,8,12,16.20
1:
2:6,10,14,18,22
3:
很明显看出有的桶有很多元素,但是有的桶是空桶,如果我们改为使用3个桶:
0: 6,12,18
1:4,10,16,22
2:2,8,14,20
可以很明显看到,我们虽然使用的桶数减少了,但是每个桶存放元素数的最大值是4,小于4个桶时的5,这在数据很多的时候会造成很大的性能差别。
分析:
在看过例子后,我们研究一下通用的解释:
假设我们使用合数作为桶数,那么桶数n除了1和n之外还会有其他的约数a1,a2….
那么假设我们的关键字和桶数有相同的约数a,那么哈希计算:k % n = (k’ *a) % (n’ *a) = (k’ % n’) *a 其中,k = k’ * a, n = n’ *a
说明计算得到的桶号肯定是a的倍数,而这些和桶数有约数的元素永远不会被放到非a倍数的桶号中,那么这时候hashtable的性能将取决于到底有多少元素能够和桶数有公约数,这对hashtable尽量使各个桶之间的元素数近似相等的原则违背。
另外,hashtable相比于RB-tree的一个优势就在于RB-tree虽然平均查找时间是对数时间,但是这是在假设数据均匀分布的基础之上的,而hashtable也有平均对数时间上的性能,且这种表现是以统计为基础,不需依赖元素输入的随机性。
而我们看到了,如果使用合数作为桶数,那么如果输入的数列为某种规律数列的话,很容易导致极端情况出现。
总结:
使用质数作为桶数,保证了我们即使是很简单的哈希函数,也可以保证整个hashteble的效率不会太低。