HashMap底层代码浅解析

我们主要看put方法,因为比较有深度。

 

 

 可以看到其中调用了putValue方法 5个参数

  参数1 Hash(key)通过计算得到Hash散列

  参数2 key:存储的key

  参数3 value:存储的Value

  参数4:代表遇到相同的key值是否替换value的值

  参数5:不太清楚。先不管

  

 1   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                    boolean evict) {
 3         Node<K,V>[] tab; Node<K,V> p; int n, i;       //这里声明了一个节点数组和一个节点,以及两个数值
 4         if ((tab = table) == null || (n = tab.length) == 0)  // 从这里看的出来。hashMap为懒加载,当对新的map中进行存储数据时,才会对map进行初始化,当表为空或者长度为0时,市容resizie进行初始化
 5             n = (tab = resize()).length;              //将初始话后hash表的容量size的值赋值给临时变量n
 6         if ((p = tab[i = (n - 1) & hash]) == null)        //判断要插入的桶是否存在节点,如果不存在,则直接把节点存储到该桶的head中  
 7             tab[i] = newNode(hash, key, value, null);      
 8         else {                            //如果既不是进行初始化,并且该桶中存在节点,那么就是发生的hash冲突。
 9             Node<K,V> e; K k;
10             if (p.hash == hash &&
11                 ((k = p.key) == key || (key != null && key.equals(k))))      //如果添加的值与头节点相同,则将新值放入其中,只是值未变化
12                 e = p;
13             else if (p instanceof TreeNode)                      //如果不同,并且该桶为红黑树状态,则条用putTreeVal添加。
14                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
15             else {                                    //如果不同,并且桶还为链表状态
16                 for (int binCount = 0; ; ++binCount) {            //对链表进行遍历
17                     if ((e = p.next) == null) {                    //当节点为空时,说明没有相同的节点,将该节点存储到链表尾部
18                         p.next = newNode(hash, key, value, null);  
19                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st      //当该节点到大8时,就将该桶转换为红黑树
20                             treeifyBin(tab, hash);
21                         break;
22                     }
23                     if (e.hash == hash &&
24                         ((k = e.key) == key || (key != null && key.equals(k))))   //如果此时已有相同的节点,那么跳出循环。
25                         break;
26                     p = e;
27                 }
28             }
29             if (e != null) { // existing mapping for key          //如果有重复的节点,那么需要返回旧值
30                 V oldValue = e.value;
31                 if (!onlyIfAbsent || oldValue == null)      //如果 onlyifAbsent为false或者旧值为null,那么将新值插入,最后返回旧值
32                     e.value = value;
33                 afterNodeAccess(e);
34                 return oldValue;
35             }
36         }
37         ++modCount;        
38         if (++size > threshold)      //当插入新节点后,判断容量如果超过阈值,则进行扩容
39             resize();      
40         afterNodeInsertion(evict);        //自类实现
41         return null;
42     }
resize()方法   --见名知意 扩容就在这里
 1  final Node<K,V>[] resize() {
 2         Node<K,V>[] oldTab = table;      //先获取老的tab
 3         int oldCap = (oldTab == null) ? 0 : oldTab.length; //判断旧表为空则旧表容量为0.
 4         int oldThr = threshold;         //将阈值赋值给临时变量          
 5         int newCap, newThr = 0;      
 6         if (oldCap > 0) {              //判断 旧表存在
 7             if (oldCap >= MAXIMUM_CAPACITY) {   //就变存在并且容量已经到达最大,那么直接返回旧表
 8                 threshold = Integer.MAX_VALUE;    然后扩容阈值为integer的最大值、
 9                 return oldTab;              //直接返回tab
10             }
11             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&       //新表容量是旧表容量左移一位,相当于乘2,并且小于表的最大容量,并且旧表容量大于16,那么新表的阈值,就等于旧表的二倍
12                      oldCap >= DEFAULT_INITIAL_CAPACITY)
13                 newThr = oldThr << 1; // double threshold
14         }
15         else if (oldThr > 0) // initial capacity was placed in threshold // 旧表阈值大于0,那么将旧表的阈值赋值给新表的存储容量,说明构造函数中指定了阈值
16             newCap = oldThr;
17         else {                   // zero initial threshold signifies using defaults      //说明旧表不存在,则对表空间进行初始化
18             newCap = DEFAULT_INITIAL_CAPACITY;            // 初始化容量              
19             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //初始化阈值。阈值为容量的75%
20         }
21         if (newThr == 0) {          //新的阈值为0,则使用新表空间的容量乘增长因子。%75,得到新的阈值
22             float ft = (float)newCap * loadFactor;          
23             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
24                       (int)ft : Integer.MAX_VALUE);
25         }  
26         threshold = newThr;        //将临时变量赋值给阈值
27         @SuppressWarnings({"rawtypes","unchecked"})      //
28             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];    //创建新表
29         table = newTab;
30         if (oldTab != null) {              
31             for (int j = 0; j < oldCap; ++j) {    //对旧表进行遍历
32                 Node<K,V> e;            //声明一个Node
33                 if ((e = oldTab[j]) != null) { //旧表中元素若不为bull,则赋值为null,方便gc
34                     oldTab[j] = null;     
35                     if (e.next == null)      //如果节点只有一个元素,将旧表节点赋值给新表节点
36                         newTab[e.hash & (newCap - 1)] = e;   
37                     else if (e instanceof TreeNode)      //如果e节点为红黑树
38                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
39                     else { // preserve order          //如果该节点仍为链表
40 Node<K,V> loHead = null, loTail = null;   41 Node<K,V> hiHead = null, hiTail = null;  
                                    那么将该节点分为低位节点,和高位节点
42 Node<K,V> next;
                                    
43 do {               44 next = e.next;              将节点值赋值给next

45 if ((e.hash & oldCap) == 0) {      //对该节点hash并与旧表容量做与运算,如果为0,则 46 if (loTail == null)          //判断低位节点尾部是否为null,如果为null,则将节点赋值到低位节点的head中
                                                
47 loHead = e; 48 else 49 loTail.next = e;          //如果低位节点尾部不为null,则将该节点赋值到低位节点尾部的下一个元素中 50 loTail = e; 51 } 52 else {                    //不为0 53 if (hiTail == null)          //判断高位节点是否为null. 54 hiHead = e;        //为null则将该节点赋值给高位节点, 55 else 56 hiTail.next = e;      //在高位节点尾部链接上该节点 57 hiTail = e; 58 } 59 } while ((e = next) != null);      //直到该链表为节点为空 60 if (loTail != null) {        //如果低位节点尾部不为bull 61 loTail.next = null;  //将低位节点的尾部的下一个元素清空为null 62 newTab[j] = loHead;        //将低位节点的头部,赋值到新表的一个桶中, 63 } 64 if (hiTail != null) {        //高位节点不为null 65 hiTail.next = null;      //将高位节点的尾部下一个值清空为null。 66 newTab[j + oldCap] = hiHead;      将高位节点的头部,赋值给新表中高位桶中。 67 } 68 } 69 } 70 } 71 } 72 return newTab; 73 }

如果表中节点已成为红黑树,那么直接在红黑数中进行赋值操作

 1    final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
                                              //参数1 map ,参数2位新表,参数3为,节点所在的索引,参数4位旧表容量
2 TreeNode<K,V> b = this;                     //头结点e          3 // Relink into lo and hi lists, preserving order           //与链表reHash中相同,将红黑树分为两个部分      4 TreeNode<K,V> loHead = null, loTail = null; 5 TreeNode<K,V> hiHead = null, hiTail = null; 6 int lc = 0, hc = 0; 7 for (TreeNode<K,V> e = b, next; e != null; e = next) {          遍历红黑树 8 next = (TreeNode<K,V>)e.next;                  //将e链的下个值赋值给next 9 e.next = null;                          //将e链的下下个节点设为空 10 if ((e.hash & bit) == 0) {                  //用e及诶按的的hash对旧有容量计算,如果为0 11 if ((e.prev = loTail) == null)      //判断低位链表尾部是否为null 12 loHead = e;                //为空,则将e节点赋值给低位节点的头节点 13 else 14 loTail.next = e;          //否则赋值给低位节点尾部节点的下一个节点 15 loTail = e;         16 ++lc;                  //低位节点长度加一 17 } 18 else { 19 if ((e.prev = hiTail) == null)        //同理 20 hiHead = e; 21 else 22 hiTail.next = e; 23 hiTail = e; 24 ++hc; 25 } 26 } 27 28 if (loHead != null) { 29 if (lc <= UNTREEIFY_THRESHOLD)          //低位节点长度不大于6则将该map转换为红海曙 30 tab[index] = loHead.untreeify(map); 31 else {           32 tab[index] = loHead;            //否则将该低位节点的头结点赋值给新表在该索引处的位置 33 if (hiHead != null) // (else is already treeified)    //如果高位节点的头节点不为null,则将该低位链表转换为红黑树 34 loHead.treeify(tab);      // 35 } 36 } 37 if (hiHead != null) { 38 if (hc <= UNTREEIFY_THRESHOLD) 39 tab[index + bit] = hiHead.untreeify(map);      高位链表类似 40 else { 41 tab[index + bit] = hiHead; 42 if (loHead != null) 43 hiHead.treeify(tab); 44 } 45 } 46 }
      如果当map容量扩大,那么reHash时,可能会有红黑树,转换为链表,此处分为高低两个链接,进程节点为存储。

将链表结构 ,转换成红黑树

 final void treeifyBin(Node<K,V>[] tab, int hash) {    //
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)    //如果表为null,或者表的长度不足64,直接进行hash扩容
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {  //判断该节点存在数据
            TreeNode<K,V> hd = null, tl = null;
            do {
          //将node转换成treeNode TreeNode
<K,V> p = replacementTreeNode(e, null);    if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) 
          //将单链表形式的节点转换为红黑树结构。    hd.treeify(tab); } }

参考:https://blog.csdn.net/qq_19431333/article/details/55505675

 
posted @ 2020-12-09 15:28  秦明科  阅读(98)  评论(0编辑  收藏  举报