HashMap扩容底层源码分析

  • 1、HashMap
    • 基于哈希表的 Map接口的实现
    • 对HashMap实例有影响其性能的两个参数:初始容量和负载因子(哈希表中的存储桶的数目:table的长度)
    • 结构:
      • JDK1.7数组+链表
      • JDK1.8数组+链表/红黑树
  • 2、 在创建HashMap对象时
    • a.初始化工作

    •  public HashMap() {
           this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
       }
      

      将负载因子设置为0.75
      table=null
      临界值=0

    • b.在第一次添加数据时,会将数组容量设置为16(JDK1.7默认初始容量为16,JDK1.8默认初始容量为0,第一次添加时变为16),并且计算出临界值为12:((int)16*0.75)

    • c.在超过hash表的临界值时,会先进行添加数据的操作,在进行扩容(扩容规则是原容量的2倍,新的临界值也是原来的2倍)
      if (++size > threshold)
      resize();

    • d.扩容完成后,会将旧数组中的数据,转移到新数组中(会重新根据hash值和新数组长度进行计算新的索引位置)

    • e.在添加数据时,如果一个桶中的链表长度大于8,并且数组长度达到64,则将当前链表结构变为红黑树结构
      如果当前桶内已经是树结构了,则按照树结构的方式去添加数据

  • 3、 在添加数据时
  •  final Node<K,V>[] resize() {
         Node<K,V>[] oldTab = table;
         int oldCap = (oldTab == null) ? 0 : oldTab.length;
         int oldThr = threshold;
         int newCap, newThr = 0;
         if (oldCap > 0) {//只有第一次添加时,不进入if的,以后的每次扩容都会进入到if中
             if (oldCap >= MAXIMUM_CAPACITY) {//如果就容量大于最大值,则采用int范围的最大值作为临界值
                 threshold = Integer.MAX_VALUE;
                 return oldTab;
             }
             //对旧容量进行2倍操作,并赋值给新容量
             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                      oldCap >= DEFAULT_INITIAL_CAPACITY)
                 //将临界值也更新为原来的2倍
                 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;
             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
         }
     	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"})
         Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//采用新容量创建hash表
         table = newTab;//将新创建的hash表赋值给table  (table就有容量了)
         if (oldTab != null) {
         	//主要是将旧数组中的数据,循环添加到新数组中,会重新分配空间
         	for (int j = 0; j < oldCap; ++j) {
         		省略...
         	}
         }
    
    

HashMap与HashTable的区别

  • 可以存储的值不同
    • HashMap:key和value都可以存null
    • HashTable:key和value都不可以存null
  • 存储方式不同
    • HashMap:JDK1.7数组+链表,JDK1.8数组+链表/红黑树
    • HashTable:数组+链表
  • 默认容量不同:
    • HashMap:JDK1.7默认初始容量为16,JDK1.8默认初始容量为0,第一次添加时变为16
    • HashTable:默认容量:11(质数为宜)
  • 扩容不同
    • HashMap:
      • ①当总元素个数超过 容量 * 负载因子(0.75)时,扩容为原来 2 倍,并重新排列元素
      • ②当单个链表的长度达到8时,继续向该链表结尾添加数据,此时:
        • 如果总容量小于64,那么扩容为原来的2倍,并重新排列元素
        • 如果总容量大于64,那么将该链表转为红黑树(当单个位置的长度小于6时转回链表)
    • HashTable:当总元素个数超过 容量 * 负载因子(0.75)时,扩容为原来 2 倍+1并重新散列
  • 线程安全性:
    • HashMap:线程不安全
    • HashTable :线程安全
  • 效率不同:
    • HashMap:效率快,不安全
    • HashTable :效率慢,因为加锁
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值