(1)HashMap的底层数据结构是什么?
haashMap最早是在jdk1.2中开始出现的,一直到jdk1.7一直没有太大的变化。但是到了jdk1.8突然进行了一个很大的改动。其中一个最显著的改动就是:之前jdk1.7的存储结构是数组+链表,到了jdk1.8变成了数组+链表+红黑树。
在jdk1.7之中把元素放在一个个数组里面, 后面存放的元素越来越多于是出现了链表,对于数组中每一个元素都可以用一个链表来存储数据.
后面元素越来越多链表越来越长直到链表长度大于8的时候转变成红黑树 查找的时候效率不仅没提高还降低不少榆树把链表变成了适合查找的红黑树
1为什么要在链表长度大于8的时候才转变成红黑树呢
1在链表节点不多的时候 数组加红黑树加链表不一定比数组加链表的效率高因此长度比较长的时候才会转变成红黑树
2.因为hashmap需要频繁扩容,会造成红黑树不断地拆分重组,这是非常耗时间的.所有只有在链表足够长才会转变成红黑树来提升效率
(2)
首先hash的put方法也是就是增加方法,第一个参数是键第二个参数是值组合起来叫做键值对
底层是首先put方法传入键值队
根据计算计算hash值
根基hash算法计算在数组中存放的位置查看是否出现了hash冲突
如果没有冲突直接存储到集和中
如果冲突查看当前列表是数组还是红黑树 红黑树的话直接插入
是链表的话判断插入是否大于等于8如果大于等于8转换为红黑树在插入 如果小于8则直接插入链表
源码
put方法是是调用了put'val方法putval方法有5个参数
第一个参数是我们计算的hash值
第二个是我们传入的key
第三个是我们传入的值
第四个是onlyIfAbsent 也就是当键相同,不修改已存在的值
第五个是evict:如果是false那么数组就处于创建者模式中 所以一般为false
那么首先putval
第一部分判断
Node<K,V>[] tab中tab表示的就是数组。Node<K,V> p中p表示的就是当前插入的节点
(2)第一部分:
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
这一部分表示的意思是如果数组是空的,那么就通过resize方法来创建一个新的数组。
然后需要判断插入的位置是否是冲突的,如果不冲突就直接newNode,插入到数组中即可
如果冲突转到第三部分,处理冲突
在首先判断table[i]中的元素是否与插入的key一样,若相同那就直接使用插入的值p替换掉旧的值e。不相同就判断插入的数据结构是红黑树还是链表,在这里表示如果是红黑树,那就直接putTreeVal到红黑树中。
如果数据结构是链表,首先要遍历table数组是否存在,如果不存在直接newNode(hash, key, value, null)。如果存在了直接使用新的value替换掉旧的。
不存在并且在链表末尾插入元素的时候,会判断binCount >= - 1。也就是判断当前链表的长度是否大于阈值8,如果大于那就会把当前链表转变成红黑树
插入成功之后,还要判断一下实际存在的键值对数量size是否大于阈值。如果大于那就开始扩容了。
(3)HashMap是如何实现扩容的?
首先如果超过了数组的最大容量,那么就直接将阈值设置为整数最大值,然后如果没有超过,那就扩容为原来的2倍
第一个else if表示如果阈值已经初始化过了,那就直接使用旧的阈值。然后第二个else表示如果没有初始化,那就初始化一个新的数组容量和新的阈值。
第三部分同样也很复杂,就是把旧数据复制到新数组里面。这里面需要注意的有下面几种情况:
A:扩容后,若hash值新增参与运算的位=0,那么元素在扩容后的位置=原始位置
B:扩容后,若hash值新增参与运算的位=1,那么元素在扩容后的位置=原始位置+扩容后的旧位置。
(4)HashMap是如何解决hash冲突的?
HashMap在JDK1.8版本中是通过链式寻址法以及红黑树来解决Hash冲突的问题,其中红黑树是为了优化Hash表的链表过长导致时间复杂度增加的问题,当链表长度大于等于8并且Hash表的容量大于64的时候,再向链表添加元素,就会触发链表向红黑树的一个转化
(7)HashMap为什么是非线程安全的?
想要解决这个问题,答案很简单,因为源码里面方法全部都是非线程安全的呀,你根本找不到synchronized这样的关键字
标签:hash,hashmap,链表,插入,数组,红黑树,如果 From: https://www.cnblogs.com/zhangseekchu/p/17709486.html