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