欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HashMap中有意思的方法!

程序员文章站 2022-04-06 23:40:00
...

关于HashMap的源码,网上文章多如沙海,本文只关注其中几个有意思的地方!

JDK版本: 1.8.0_151

知识点盘点

1.  HashMap中有意思的方法!的二进制表示,只有最高位是1,其余的都是0,而HashMap中有意思的方法! 则全部二进制位都是1,如下图所示

 HashMap中有意思的方法!

2.  Java中的位运算符:

  • &: 位与操作,全1则1,有0则0
  • | : 位或操作, 有1则1,全0则0
  • ^:    异或操作,相同则1,不同则0
  • >> : 右移,如果这个数是正数,则高位补0,如果是负数,则高位补1
  • >>>: 无符号右移,无论正负,高位补0
  • <<: 左移,不分正负,低位补0

3.  一点儿结论:

  • 一个任意的int值a对HashMap中有意思的方法! 进行&操作,即 a & (HashMap中有意思的方法!),结果等同于 a%HashMap中有意思的方法!。就是取模值;
  • 一个任意的int值a对HashMap中有意思的方法!进行&操作,即 a & HashMap中有意思的方法! ,结果如果是0,那么 a % HashMap中有意思的方法! = a % (HashMap中有意思的方法!*2),如果不是0,那么 a % (HashMap中有意思的方法!*2)=  a % HashMap中有意思的方法!  +  HashMap中有意思的方法! 

 

HashMap中的方法

1. 取hash的方法

// put之前,先对key进行取hash操作
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

// 扰动函数,尽量避免hash碰撞
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

  从取hash的方法中,可以看出如果key是null,则直接返回0,所以map允许存null,
  如果key不是null,则就取key的hashCode与hashCode >>> 16的 异或操作值。称这个函数为扰动函数。

2. tableSizeFor(int cap) 根据传入值获取大于此值的最小的2的m次方。

/**
 * Returns a power of two size for the given target capacity.
 */
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

首先  n |= n >>> 1 相当于 n = n | n >>> 1 。 首先n值的最高位肯定是1,右移1位,做位或操作的结果是,第1,2高位是1;然后右移2位,做位或操作的结果是,第1,2,3,4位是1,右移4位,同理,前8位一定是1, 最终理论上所有位上都会变成1, 最终在加上1,则是大于这个n值的最小2的m次方;(关于最大值校验,则不再叙述)如图

HashMap中有意思的方法!

3.  putVal()方法中的取模操作

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 {
...
}

我们关注在(n - 1) & hash这个操作上,首先n是tab.length(),也就是hashMap中的“哈希桶”的数量,这个值一定是2的m次方,hash值与(n-1)进行&操作后的结果就是 hash%n ,也就是这个key应该在的桶的索引。

 

4. resize()方法,2倍扩容方案

final Node<K,V>[] resize() {
   ...
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                // 清空原来的桶的node,此时原来的Node值已经赋给了e
                oldTab[j] = null;
                // 如果只有这一个值,那么直接进行和put方法中相同的hash操作即可。
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                // 如果是红黑树,则进行红黑树的rehash操作,流程和下面的链表一样。
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        // 有意思的地方!!!
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}
...
}

我们只关注标注中有意思的部分,key的hash对oldCap进行 & 操作,结果是啥呢? 因为oldCap是2的m次方,所以它的二进制表示中,只有最高位是1, 所以 hash & oldCap的结果不是0就是 HashMap中有意思的方法!。而对这两种结果,有一下特点,如图:

HashMap中有意思的方法!

如果不对之处,请大家指出,谢谢!!

相关标签: hashmap