HashMap
刨析HashMap—>往祖坟上刨的那种!
扩展:
什么是哈希冲突:
先介绍哈希表:键 :值 存储结构,通过key查找值(貌似是字典)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZPMS3vjq-1678839478547)(D:\前锋学习笔记\笔记\屏幕截图 2023-03-14 204514.png)]
AB和BA 的地址发生的相同(碰撞情况),有两种方法解决,开放寻址,拉链法
拉链法:把AB放入以后,再放入BA发现有碰撞,就让存储AB的空间再存储个BA的地址空间,依次内推,直到不碰撞为止。
JDK1.7由 数组+链表(解决哈希冲突)
JDK1.8 数组+链表+红黑树
实现介绍一下(数组:有序-连续的)(链表:无顺序通过指针连接)(红黑树)的特点:
一:查询
(1):数组是按下标查询的,查询比较快。
(2):链表是从第一个的元素查找,直到查到需要的元素。
(3):平衡二叉树的遍历
二:插入
(1):数组的长度是先固定好的,需要移动其他元素。
(2):链表是可以动态的增减
(3):左旋右旋(保存平衡)
Hash的特点
又叫散列;具备数组的快速查询特点,又有链表的增加删除的元素的特点。
结合了数组和链表的优点。
JDK1.8为什么要添加上红黑树
数组和链表已经很方便了为什么?还要加上红黑树呢?是什么原因导致要加入红黑树?
答:上面扩展中提到了,解决碰撞的办法在链表上添加,但是当链表的长度过长时效率又低了,为了解决这个问题,当链表过长时,会让链表转为红黑树。
转换的条件先判断(链表超过8,并总量超过64)
为什么要按照数组,链表,红黑树这个顺序存储数据?
因为数组读写速度快,但是插入和删除时间复杂度大 还会产生碰撞,就替换成链表发挥他的插入和删除的优点,但是链表过长也会影响速度,这时候就用红黑树啦解决这个问题了。
如果直接使用红黑树的话增加新的节点和变色,左旋,右旋,保持平衡等操作直接就把增加新的节点速度变慢了。元素多的时候还不是太明显,一旦元素少的话,就相对比较下太慢了。
总结:后一个的数据结构是为了解决前一个数据结构在最坏情况(场景)下的最优处理方法。
加载因子和阈值
初始化是16
阈值是0.75
容量超过负载因子后,16*0.75 = ,会扩大为原来的两倍。
当然可以修改,一般不建议,除非是特殊的空间结构情况下。
// jdk1.8 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); /* h = key.hashCode() 为第一步:取hashCode值 h ^ (h >>> 16) 为第二步:高位参与运算 */ }
为什么线程不安全
多线程下扩容死循环。容易产生环形链表
多线程的put可能导致元素的丢失。可能导致值覆盖的情况
put和get并发时,可能导致get为null。
put 过程
- 首先根据 key 的值计算 hash 值,找到该元素在数组中存储的下标;
- 如果数组是空的,则调用 resize 进行初始化;
- 如果没有哈希冲突直接放在对应的数组下标里;
- 如果冲突了,且 key 已经存在,就覆盖掉 value;
- 如果冲突后,发现该节点是红黑树,就将这个节点挂在树上;
- 如果冲突后是链表,判断该链表是否大于 8 ,如果大于 8 并且数组容量小于 64,就进行扩容;如果链表节点大于 8 并且数组的容量大于 64,则将这个结构转换为红黑树;否则,链表插入键值对,若 key 存在,就覆盖掉 value。