HashMap源码解析

本文深入解析HashMap在JDK1.7和1.8的区别,包括1.7的数组+链表结构导致的性能问题,1.8引入红黑树解决冲突及扩容优化,以及1.7扩容可能导致的死循环问题。详细阐述了HashMap的属性、put()操作、计算方法以及扩容策略。
摘要由CSDN通过智能技术生成

总览

  1. JDK1.7和JDK1.8的区别
  2. 属性解释
  3. put()过程解析
  4. 计算threshold
  5. JDK1.8扩容优化
  6. JDK1.7死循环图解

区别

JDK1.7
  • 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。
  • 当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,极端情况HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
  • 数组形成链表,新的节点添加在头节点(会有死循环)
JDK1.8
  • JDK7与JDK8中HashMap实现的最大区别就是对于冲突的处理方法。JDK 1.8 中引入了红黑树(查找时间复杂度为 O(logn)),用数组+链表+红黑树的结构来优化这个问题。
  • 数组形成链表,新的节点添加在尾节点
  • resize()不需要重新rehash()寻址
  • 解决了resize()时多线程死循环问题,但仍是非线程安全的
  • 数组形成链表,新的节点添加在尾节点

属性解释

属性定义
  • transient Node<K,V>[] table HashMap的哈希桶数组,非常重要的存储结构,用于存放表示键值对数据的Node元素
  • transient Set<Map.Entry<K,V>> entrySet HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能
  • transient int size HashMap中实际存在的Node数量,注意这个数量不等于table的长度,甚至可能大于它,因为在table的每个节点上是一个链表(或RBT)结构,可能不止有一个Node元素存在
  • transient int modCount HashMap的数据被修改的次数,这个变量用于迭代过程中的Fail-Fast机制,其存在的意义在于保证发生了线程安全问题时,能及时的发现(操作前备份的count和当前modCount不相等)并抛出异常终止操
  • final float loadFactor 也是加载因子,衡量HashMap满的程度,当实际大小超过临界值时,会进行扩容,默认0.75
  • int threshold 达到临界值,当元素达到临界值会进行扩容2倍,threshold = 加载因子*容量
默认属性
  • static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 默认初始大小16
  • static final float DEFAULT_LOAD_FACTOR = 0.75f 默认扩容是按照原容量的0.75倍进行扩容
  • static final int TREEIFY_THRESHOLD = 8 当某个桶节点大于8,且总数超过64,转化为红黑树,否则扩容
  • static final int UNTREEIFY_THRESHOLD = 6 当某个桶节点小于6时,会转化为链表,前提它是红黑树
  • static final int MIN_TREEIFY_CAPACITY = 64 当整个HashMap中的元素数量大于64时,且某个桶节点大于8,也会转化为红黑树结构
  • static final int MAXIMUM_CAPACITY = 1 << 30 最大容量

put()解析

public V put(K key, V value) {
   
    return putVal(hash(key), key, value, false, true);
}

发生冲突时,链表中新节点jdk1.7中是放在首位,jdk1.8是放在尾节点

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,  //这里onlyIfAbsent表示只有在该key对应原来的value为null的时候才插入,也就是说如果value之前存在了,就不会被新put的元素覆盖。
               boolean evict) {
   
    Node<K,V>[] tab; Node<K,V> p; int n, i;                     //定义变量tab是将要操作的Node数组引用,p表示tab上的某Node节点,n为tab的长度,i为tab的下标。
    // 将成员变量 table 赋值给本地变量 tab,并且将tab的长度赋值给本地变量 n
    // 如果tab为空或者 数组长度为0,进行初始化,调用 resize()方法,并且获取赋值后的数组长度
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值