要计算HashMap中对应的数组下标,必然要进行获取哈希值,HashMap对key的哈希值做了离散处理。
JDK 7
哈希值计算:
/**
* Retrieve object hash code and applies a supplemental hash function to the
* result hash, which defends against poor quality hash functions. This is
* critical because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ
* in lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
下标计算:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
JDK 8
哈希值计算:
/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower. Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.) So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
下标计算:(put方法中摘抄)
n = tab.length
i = (n - 1) & hash
哈希值计算:
- JDK 7:使用哈希种子,将原哈希值和左移20位、左移12位、左移7位、左移4位的四个值,一起异或运算(^)。
- JDK 8:将原哈希值和左移16位的值,一起异或运算(^)。
哈希种子(hashSeed),一个和实例有关的随机数,添加离散程度,可以被禁用,为0。通过vm参数 ‘-Djdk.map.althashing.threshold=7’,当数组容量大于7时,提供哈希种子。
JDK 7时,对key等于null时,是固定放在下标位0的位置,不参加哈希值计算,
到了JDK 8时,key等于null时,是在哈希值计算中处理,返回哈希值为0。最终结果一直,只是处理过程不一样。
下标计算:
算法一样。都是再数组长度减一后,做与运算(&)。因为配合数组长度位2的次方数,所以相当于length-1取余。
例子:
计算后的哈希值 x,length=16,下标10.
x : 0010 0011 1111 1100 0110 0010 0101 1010
length-1 : 0000 0000 0000 0000 0000 0000 0000 1111
&
index : 0000 0000 0000 0000 0000 0000 0000 1010