【Java学习笔记】HashMap 学习 (JDK8)

1.hashmap中hash函数的实现中,异或运算操作的结果是什么,为什么要做这样的异或运算

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

假设h是 1000010001110001000001111000000,h>>>16的结果就是一个新的32位数,高16位为0,低16位为h的高16位,这个新数记为q,h^q的结果记为r,以方便描述

由于异或运算的特点,r的高16位就是h的高16位,r的低16位,就是h的低16位和h的高16位进行异或运算的结果

为什么要这样做呢,因为在putVal函数里,在为newNode找坑时,下标 i = (n - 1) & hash ,之所以要这样去计算i,是因为hash值是一个可能大于table数组大小的值,为了解决越界问题,也可以用取模的方式计算出合适的i,但是取模运算没有与运算效率高,而这里的与运算可以达到类似的效果,让计算出的i不会越界

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

同时这也引出一点,就是为什么table的数组被要求是2的幂。

HashMap里table的初始大小是16, 二进制也就是 16-1=15,二进制表示是 1111,和hash值与运算,能保留hash值低4位的值,相当于保留了此前hash的散列结果

如果将HashMap里table的初始大小设置位17,17 - 1 = 16,二进制表示是10000,和hash值与运算,会破坏hash值的本身的结果,显然不会是一个好的散列方式

原文地址:https://www.cnblogs.com/heben/p/10761496.html