Java集合之HashMap PART II

40 阅读2分钟

Java 8及之后版本的实现:

static final int hash(Object key) {
        int h;
        return key == null ? 0 : (h = key.hashCode()) ^ h >>> 16;
}

此处的hash方法需要解释一下:

>>>是无符号右移

0000 0100 1011 0011  1101 1111 1110 0001
 
>>> 16 
 
0000 0000 0000 0000  0000 0100 1011 0011

>>>的运算符优先级要高于^

hashCode()是一个native方法,返回一个长度32位的值。

为何需要在hash方法里做如此操作呢?

hash方法其实用来返回一个数组下标。我们来看下代码片段:

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node[] tab;
        int n;
        if ((tab = this.table) == null || (n = tab.length) == 0) {
            n = (tab = this.resize()).length;
        }

        Object p;
        int i;
        if ((p = tab[i = n - 1 & hash]) == null) {
            tab[i] = this.newNode(hash, key, value, (Node)null);
        }

我们会看到n - 1 & hash

由于我们使用位掩码 ((n - 1) & hash) 计算模数,因此模数不会使用高于 n - 1 最高位的任何位。例如,给定n=32和4个哈希码来计算。不经过哈希码变换直接取模时,所有索引都为1,碰撞率为100%。这是因为掩码 31 (n - 1), 0000 0000 0000 0000 0000 0000 0001 1111,使得任何高于位置 5 的位在数字 h 中不可用。为了使用这些最高位,HashMap 将它们向左移动 16 个位置 h >>> 16 并以最低位展开 (h ^ (h >>> 16))。结果,获得的模具有较少的冲突。

直白点讲:

由于和(length-1)运算,length 绝大多数情况小于2的16次方。所以始终是hashcode 的低16位(甚至更低)参与运算。要是高16位也参与运算,会让得到的下标更加散列。 所以这样高16位是用不到的,如何让高16也参与运算呢。所以才有hash(Object key)方法。让他的hashCode()和自己的高16位^运算。所以(h >>> 16)得到他的高16位与hashCode()进行^运算。