取模运算转位运算
记录了学习散列函数中运算转换时遇到的问题,这里主要是一开始没想清楚取模运算如何转成位运算
问题场景
最近看到Java HashMap中散列函数的设计有以下代码
int hash(Object key) {
int h = key.hashCode();
return (h ^ (h >>> 16)) & (capicity -1);
}
哈希表别的地方光看理论似乎还是能看懂(还没理解清楚),但是这里的求与运算一开始没想清楚是如何转换成取模运算的,遂记录笔记理清思路
首先散列函数hash计算哈希值使数据能均匀分布在哈希表中,hashCode方法为获取用于唯一识别对象的哈希码(所以重写equals方法也必须重写hashCode)。
例如String的hashCode是将每一个字符转换成31进制的整数值
获取哈希码后散列函数基于以下三点设计
- 尽量减少散列冲突 (采用作为奇质数的31进制返回哈希码)
- 原对象有变动则哈希值大不相同 (高低位异或以扰动整体二进制码,避免数值微弱变动导致冲突概率变大)
- 与运算相当于取模,只保留31进制整数的低位值用于下标访问 (哈希表长度为2的整数幂)
现在理清楚了散列函数的其余部分,但初看一时还是未能理解为什么与运算相当于取模(等于如下算式)
n % ( 2 k 2^k 2k) = n & ( 2 k − 1 2^k-1 2k−1) = n & ((1<<k)-1) ,1左移k位等于 2 k 2^k 2k
列了好几个例子才弄懂,这里单独写一份
e.q.
21 % 8 = 21 / 8 = 10101 - 1000 = 10101 - (1000<<1) = 101 = 5
21 & 7 = 10101 & 00111 = 101 = 5
首先想起来取模是除了之后看余数,然后写成原码后一看。偶数可以直接移位相乘得出低位的余数,与运算则直接跳过了相乘补位这一步所以效率更高。