为了能到远方,脚下的每一步都不能少.|

岁月记忆

园龄:3年8个月粉丝:2关注:3

对HashMap的理解

  1. jdk8之后的hashMap基于数组+链表+红黑树结构构成

  2. hashMap初始化长度为16,之后的每一次扩容都翻倍扩。

  3. hashMap内部维护了一个增长因子,默认为0.75;集合中保存的元素的个数 >= 数组长度 * 0.75后就会扩容

  4. 每次在调用map集合的put方法时,首先根据键的hashCode方法,计算出其在数组中的对应下标

  5. 如果数组下标处没有保存元素,则直接已Node对象的形式保存在该下标处

  6. 如果数组下标处已经有保存元素,如果该处是一个链表,则直接将新插入的元素保存在链表中(尾插法);如果链表的长度大于等于8,并且数组长度大于等于64,则链表进化为红黑树。否则对数组进行扩容,而不转换为红黑树

    如果该下标处不是链表,而是红黑树,则将新插入的节点插入到红黑树中;如果在删除元素时,到红黑树的长度不足6时,则红黑树退化为链表。

Map<String,Object> map = new HashMap<>();
map.put("a",1)

源码分析:

//用于保存集合数据的Node对象数组
transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
       final int hash; //键经过计算后的hash值
       final K key;   //键
       V value;       //值
       Node<K,V> next; //下一个Node节点的引用。形成链表的关键属性
}


public HashMap() { //无参构造方法
   this.loadFactor = DEFAULT_LOAD_FACTOR; // 将默认的增长因此 0.75赋值给属性loadFactor
}
map.put("f", 6); //调用put方法,将键及值插入到map集合
map.put("f", 7); // map.get("f")   值是多少? 6

源码分析:

public V put(K key, V value) {
   //hash(key) 计算键的hash值
   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);
}

/*真正实现将键和值插入到map集合的方法*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
              boolean evict) {
   Node<K,V>[] tab; //准备一个空的Node数组,将来和table会做关联
   Node<K,V> p; //准备一个空的Node对象
   int n, i;
   //检测table数组是否为空
   if ((tab = table) == null || (n = tab.length) == 0)
       //计算新数组的长度
       n = (tab = resize()).length; // resize()会在集合中尚未插入元素时执行一次   16
   /*
  15&hash -> 不管hash值是多少,最终算出来的结果必定是0-15之间
   */
   if ((p = tab[i = (n - 1) & hash]) == null)//根据hash计算该hash对应的下标处是否有存在元素
       tab[i] = newNode(hash, key, value, null); //该下标没有保存Node,则创建Node并保存到该下标
   else {//下标为i出已经保存了一个Node节点
       Node<K,V> e; //临时的Node节点
       K k; //临时的键
       if (p.hash == hash && //发生hash碰撞,键重复
          ((k = p.key) == key || (key != null && key.equals(k))))
           e = p;
       else if (p instanceof TreeNode) //判断节点是否为红黑树
           //如果当前节点p是一个TreeNode类型(红黑树),则直接已树的形式插入
           e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
       else {//将新的node节点已链表的形式插入到集合
           for (int binCount = 0; ; ++binCount) {
               if ((e = p.next) == null) {//获取链表的尾部
                   //尾插法,将Node插入到链表的最后
                   p.next = newNode(hash, key, value, null);
                   //如果链表的长度>=7,则将链表树化
                   if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                       //将链表转换为红黑树
                       treeifyBin(tab, hash);
                   break;
              }
               //判断,避免链表中的Node存在重复的键
               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;//获取原始的value值
           if (!onlyIfAbsent || oldValue == null)
               e.value = value;//将新的value值替换给相同key的value值
           afterNodeAccess(e);
           return oldValue; //返回旧的值
      }
  }
   ++modCount;
   if (++size > threshold)
       resize();
   afterNodeInsertion(evict);
   return null;
}


final Node<K,V>[] resize() {
   Node<K,V>[] oldTab = table; // null
   int oldCap = (oldTab == null) ? 0 : oldTab.length; //0,就数组的长度
   int oldThr = threshold; //扩容的阈值,0
   int newCap, newThr = 0; // 数组的新长度变量和新扩容阈值
   if (oldCap > 0) {
       if (oldCap >= MAXIMUM_CAPACITY) {
           threshold = Integer.MAX_VALUE;
           return oldTab;
      }
       else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                oldCap >= DEFAULT_INITIAL_CAPACITY)
           newThr = oldThr << 1; // double threshold
  }
   else if (oldThr > 0) // initial capacity was placed in threshold
       newCap = oldThr;
   else {               // zero initial threshold signifies using defaults
       newCap = DEFAULT_INITIAL_CAPACITY; //默认初始化数组长度,16
        //计算长度为16时,数组的扩容阈值 12
       newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//当集合中保存元素到达12个时,就会对table数组扩容
  }
   if (newThr == 0) {
       float ft = (float)newCap * loadFactor;
       newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                (int)ft : Integer.MAX_VALUE);
  }
   threshold = newThr;//将计算出结果赋值给扩容阈值
   @SuppressWarnings({"rawtypes","unchecked"})
   //创建了一个长度为16的空的Node数组
   Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
   table = newTab;//将新数组的引用赋给了table
   if (oldTab != null) {
       for (int j = 0; j < oldCap; ++j) {
           Node<K,V> e;
           if ((e = oldTab[j]) != null) {
               oldTab[j] = null;
               if (e.next == null)
                   newTab[e.hash & (newCap - 1)] = e;
               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;
}
 

本文作者:岁月记忆

本文链接:https://www.cnblogs.com/huang2979127746/p/16657966.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   岁月记忆  阅读(162)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起