HashMap类主要用来处理具有键值对特征的数据。随着JDk版本的更新,JDk1.8对HashMap底层也进行了优化,我们来结合原码,一起深入浅出吧!!
HashMap是基于哈希表对Map接口的实现,HashMap具有较快的访问速度,但是遍历顺序却是不确定的
HashMap 提供所有可选的映射操作,并允许使用 null 键 和 null 值
new HashMap().put(null,null) √
HashMap 并非线程安全,当存在多线程同时写入 HashMap 时,可能会导致数据不一致的情况。
loadFactor 成为负载因子,默认值0.75
threshold 表示所能容纳的临界值
threshold 计算公式为 数组长度 * 负载因子
size 是hashMap 中实际存在的键值对数量
modCount 用于记录HashMap内部结构发生变化的次数
HashMap 默认容量INITIAL_CAPACITY为16
HashMap 采用了数组 + 连表 + 红黑树(jdk 1.8)的存储结构
HashMap 数组部分称为哈希桶,当连表长度大于8时,连表数据将以红黑树的形式进行存储,当长度降为6时,转为连表
连表时间复杂度 为 O(n)
红黑树的时间复杂度为 O(log n)
每个Node节点存储着用来定位数据索引位置的hash值,k键,V值以及指向链表下一个节点的Node<k,V> next节点
组成。
Node是HashMap的内部类,
实现了Map.Entry接口本质是一个键值对
当向HashMap中插入数据时,首先要确定在哈希桶数组中的位置
如何确定Node的存储位置呢? ? ? ?
以添加一个Key键为字符 ’e’ 为例
HashMap 首先调用 hashCode() 方法,获取 key 的 hashCode 值 h(101),然后进行高位运算:
将 h 左移16 位 以获取 h 高16 位 ,与原 h 的低16位进行异或运算(结果为 101)
最后将得到的h值与(table.length -1)进行与运算获取对象的保留为以计算下标
例如存放键key 分别为 ‘a’ , ‘b’ , ‘d’ , ‘r’ , ‘t’ , ‘e’ , ‘a’ , ‘g’ , 'i’对象
通过计算知 ‘a’ 的下标为1(0001),
'b’为2 (0010),
'd’为4(0100),
'r’为2(0010),
't’为4(0100),
'e’为5(0101)
当插入第二个以’a’为key的对象时,将新直赋值给’a’的值
当插入的对象大小超过临界值时,HashMap将新建一个桶数组
并重新赋值(jak1. 7和1.8重新赋值方式略有不同)
插入 ‘a’ , ‘b’ , ‘d’ , ‘r’ , ‘t’, ‘f ’ , ‘u’,‘w’ ,‘e’ , ’ j’ , ‘g’ , ‘i’ , ‘k’ ,null