HashMap的put过程和底层数据结构
1.hashMap的概念和底层结构
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。HashMap储存的是键值对,HashMap很快。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap 内部结构:
jdk1.8以前可以看作是 “数组"和"链表” 结合组成的复合结构,数组被分为一个个桶(bucket),每个桶存储有一个或多个Entry对象,每个Entry对象包含三部分key(键)、value(值),next(指向下一个Entry),每个桶存储有一个或多个Entry对象,每个Entry对象包含三部分key(键)、value(值),next(指向下一个Entry)
jdk1.8的时候 “数组"和"链表"和"红黑树” 为如果链表大小超过树形转换的阈值(TREEIFY_THRESHOLD= 8),链表就会被改造为树形结构。
hashMap的结构示意图如下:
查询时间复杂度:HashMap的本质可以认为是一个数组,数组的每个索引被称为桶,每个桶里放着一个单链表,一个节点连着一个节点。很明显通过下标来检索数组元素时间复杂度为O(1),而且遍历链表的时间复杂度是O(n),所以在链表长度尽可能短的前提下,HashMap的查询复杂度接近O(1)
数组:存储区间连续,占用内存严重,寻址容易,插入删除困难;
链表:存储区间离散,占用内存比较宽松,寻址困难,插入删除容易;
Hashmap综合应用了这两种数据结构,实现了寻址容易,插入删除也容易。
2.hashMap的工作原理
HashMap的工作原理 :HashMap是基于散列法(又称哈希法)的原理,使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket(桶)位置来储存Entry对象。
键的hash碰撞问题:
两个元素通过hash函数计算出的值是一样的,是同一个存储地址。当后面的元素要插入到这个地址时,发现已经被占用了,这时候就产生了hash冲突
hashMap采用链地址法:
当冲突时,在冲突的地址上生成一个链表,将冲突的元素的key,通过equals进行比较,相同即覆盖,不同则添加到链表上,此时如果链表过长,效率就会大大降低,查找和添加操作的时间复杂度都为O(n)
所以在JKD1.8中对这种情况进行了控制,当一个hash值上的链表长度大于8时,该节点上的数据就不再以链表进行存储,而是转成了一个红黑树
3.几个重要的参数
主干数组的初始容量为16:
默认加载因子为0.75
临界值=主干数组容量*负载因子 大于这个临界值时bucket数组会进行扩容 bucket数组的大小在扩容前后都将是2的次幂
如果主干数组上的链表的长度大于8,链表转化为红黑树
hash表扩容后,如果发现某一个红黑树的长度小于6,则会重新退化为链表
当hashmap容量大于64时,链表才能转成红黑树