面试遇到一个问题:hashmap将值映射到hash数组上的具体过程,答得不好,所以在此重新梳理一遍。
第一个过程是通过键计算出hash值的过程。即下面hash(key)的过程。
HashMap源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
由此可见:hashmap通过:(h = key.hashCode()) ^ (h >>> 16)计算出hash值。
hashCode()是Object的一个Java内部封装的方法,它会根据特定规则产生一个32位的hashcode。
为什么这样计算呢? 因为哈希数组的长度远远达不到2^16.后续通过hash计算在hash数组的位置时用不到高16位。通过这种无符号右移+异或方式,HashMap
的哈希函数将原始哈希码的高位信息与低位信息结合在一起,产生了更加均匀分布的新哈希值。这有助于在 HashMap
的内部数组中更均匀地分布数据,从而减少了哈希冲突,提高了数据结构的整体性能。
Object的hashcode方法:
第二个过程是通过hash值计算出hash数组索引的过程:
计算过程:
p = tab[i = (n - 1) & hash}]
p即为所要插入到的hash数组的位置的索引,通过(n-1)& hash计算得出。n为数组的长度。
hashmap规定数组的长度必须为2的倍数,当长度为2的倍数时,hash % n = hash & (n – 1),
与数组长度进行取余计算就是非常经典的一个计算索引位置的方法。
为什么要采用2进制的方法计算呢?
位运算的计算效率更高。