JDK1.8HashMap源码学习

234 阅读5分钟

实现原理:

将KEY放入数组 -> 计算hash码 -> 经过(n - 1) & hash运算 -> 得出在数组中的下标

1. 构造函数

1.1 无参创建HashMap对象

image.png

1.1.1 进入到构造函数中

image.png

1.2 有参构造hashmap

image.png

1.2.1 进入到构造函数内部

image.png

1.2.2 继续调用本类的构造函数

image.png

1.2.3 tableSizeFor()函数

调用tableSizeFor函数,这个函数的主要作用就是确定数组的长度,返回的长度是最接近参数的2的N次幂,即当cap=10,返回的是2的4次幂,即16

    // 确定主数组的长度,返回的长度是最接近参数的2的N次幂,即当cap=10,返回的是24次幂,即16
    // cap = 10
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        // n = 9(00000000 00000000 00000000 00001001)
        /*
         00000000 00000000 00000000 00001001
         >>> 1
    ==============================================
         00000000 00000000 00000000 0000100
​
        00000000 00000000 00000000 00001001
      |
        00000000 00000000 00000000 00000100
    ===============================================
        00000000 00000000 00000000 00001101 = 13
         */
        n |= n >>> 1;
        /*
         00000000 00000000 00000000 00001101
         >>> 2
    ==============================================
         00000000 00000000 00000000 00000011
​
        00000000 00000000 00000000 00001101
      |
        00000000 00000000 00000000 00000011
    ===============================================
        00000000 00000000 00000000 00001111 = 15
         */
        n |= n >>> 2;
    /*
         00000000 00000000 00000000 00001111
         >>> 4
    ==============================================
         00000000 00000000 00000000 00000000
​
        00000000 00000000 00000000 00001111
      |
        00000000 00000000 00000000 00000000
    ===============================================
        00000000 00000000 00000000 00001111 = 15
    */
        n |= n >>> 4;
    /*
         00000000 00000000 00000000 00001111
         >>> 8
    ==============================================
         00000000 00000000 00000000 00000000
​
        00000000 00000000 00000000 00001111
      |
        00000000 00000000 00000000 00000000
    ===============================================
        00000000 00000000 00000000 00001111 = 15
    */
        n |= n >>> 8;
    /*
         00000000 00000000 00000000 00001111
         >>> 16
    ==============================================
         00000000 00000000 00000000 00000000
​
        00000000 00000000 00000000 00001111
      |
        00000000 00000000 00000000 00000000
    ===============================================
        00000000 00000000 00000000 00001111 = 15
    */
        n |= n >>> 16;
        // 通过上面的位运算,n的结果为15;
        // n=15,三位运算的结果为 n+1,即15+1 = 16
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

此时,有参的hashmap已经创建完成;

2. 添加元素

2.1 第一个元素

image.png

2.1.1 进入到put函数中

image.png

2.1.2 hash()函数

image.png

2.1.3 putVal()函数

image.png

2.1.4 进入到resize函数中

image.png

image.png

resize函数走完,相当于在底层创建了一个长度为16的空数组

image.png

继续从第24步往下走

image.png

添加完成,数组中有了一个元素

image.png

2.2 key值和计算后的hash值都不一样

往map中添加key值和计算后的hash值都不一样的步骤,和第一次添加元素基本一样,直接在对应下标添加数据即可

image.png

2.3 key值一样,value不一样

往数组中添加key值一样,value不一样的数据

2.3.1 调用put()

image.png

2.3.2 进入putVal()

重新进入到putVal方法中,关键代码

image.png

将数组中原有数据的指向p赋值给e后,直接进入到下面代码

image.png

image.png

2.4 key值不一样,hash值一样

往map中添加key值不一样,但是hash值一样的键值对

假设 “李四” 的hash值与 “张三” 一样

2.4.1 调用put()方法

image.png

2.4.2 putVal()方法

再次进入到putVal函数中

image.png

image.png

image.png

2.4.3 treeifyBin 树化操作

image.png

3. 总结:

  1. 元素key唯一;
  2. 元素无序;
  3. 当放入重入key元素时,key是第一个数据的key,value是第二个数据的value;
  4. 放入自定义数据类型时,需要重写hashCode()和equals();
  5. key值可以存放null;
  6. hash算法:(n - 1) & hash