HashMap知识点
1、HashMap的内部数据结构
JDK1.8版本的,内部使用数组 + 链表 / 红黑树
2、HashMap的数据插入原理
- 判断数组是否为空,为空进行初始化
- 不为空,计算k的hash值,通过(n-1)&hash计算应当存放在数组下标的index
- 查看table[index]是否存在数据,没有数据就构造一个Node节点存放在table[index]中
- 存在数据,说明发生了hash冲突,继续判断key是否相等,相等,用新的value替换原数据(onlyIfAbsent为false)
- 如果不相等,判断当前节点类型是不是树型节点,如果是是树型节点,创建树型节点插入红黑树中
- 如果不是树型节点,创建普通Node加入链表中;判断链表长度是否大于8,大于的话链表转换为红黑树
- 插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的2倍
3、HashMap初始容量大小
一般如果new HashMap()不传值,默认大小为16,负载因子是0.75,
如果自己传入初始大小k,初始化大小为大于k的2的整数次方
4、HashMap的哈希函数怎么设计的
hash函数先拿到通过key的hashcode,是32位的int值,然后让hashcode的高16位和低16位进行异或操作
这样设计原因有两点:
(1)一定要尽可能降低hsah碰撞,越分撒越好
(2)算法一定要尽可能高效,因为这是高频操作,因此采用位运算
5、1.8的优化
- 数组+链表改成了数组+链表或红黑树
- 链表的插入方式从头插法改为尾插法,简单的说就是插入时,如果数组位置上已经有元素,1.7将新元素放到数组中,原始节点作为新节点的后继节点,1.8遍历链表,将元素放置到链表的最后
- 扩容的时候1.7需要对原数组中的元素进行重新hash定位在新数组的位置;1.8采用更简单的逻辑判断,位置不变或索引+旧容量大小
- 在插入时,1.7先判断是否需要扩容,再插入;1.8先进行插入,插入完在判断是否需要扩容
6、为什么做优化
-
防止发生hash冲突,链表长度过长,将时间复杂度由
O ( n ) O(n) O(n)
将为
O ( l o g n ) O(logn) O(logn) -
因为1.7头插法扩容时,头插法会使链表发生转移,多线程环境下会产生环
例如:A线程在插入节点B,B线程也在插入,遇到容量不够开始扩容,重新hash,放置元素,采用头插法,后遍历到的B节点放入了头部,这样就形成了环:
-
扩容的时候为什么1.8不用重新hash就可以直接定位原节点在新数据的位置呢?
这是由于扩容时扩大为原数组大小的2倍,用于计算数组的掩码仅仅只是高位多了一个1,因为是&运算,1和任何数&都是它本身
7、HashMap是线程安全的么
不是,在多线程环境下,1.7会产生死循环、数据丢失、数据覆盖的问题。1.8中会有数据覆盖的问题
8、怎么解决HashMap线程不安全
java中有Hashtable、Collections.synchronizedMap、以及ConcurrentHashMap可以实现线程安全的Map
- Hashtable是直接在操作方法上加上synchronized关键字,锁住整个数组,粒度比较大
- Collections.synchronizedMap是使用Coolections集合工具的内部类,通过传入map封装出一个synchronizedMap对象,内部定义了一个对象锁,方法内通过对象锁实现
- ConcurrentHashMap使用分段锁,降低了锁粒度,让并发度大大提高
9、链表转红黑树是链表长度达到阈值是多少
链表转红黑树表阈值是8
红黑树转链表的阈值是6
因为经过计算,在Hash函数设计合理的情况下,发送hash碰撞8次的几率是万分之6,所有8够用了,
至于为什么转回来是6,因为如果hash碰撞次数在8附件徘徊,会一直发送链表和红黑树的转化,为了预防这种情况的发生
10、HashMap内部节点是无序的,根据hash值随机插入
11、LinkedHashMap和TreeMap是有序的
12、LinkedHashMap如何实现有序
LinkedHashMap内部维护了一个单链表,有头尾结点,同时LinkedHashMap节点Entry内部除了继承HashMap的Node属性,还有before和after用于标识前置节点和后置节点。可以实现按插入的顺序或访问顺序排序。
13、TreeMap如何实现有序
TreeMap是按照Key的自然顺序或者Comprator的顺序进行排序,内部是通过红黑树来实现。所以要么key所属的类实现Comprator接口,或者自定义一个实现了Comprator接口的比较器,传给TreeMap用户key的比较
14、HashMap和Hashtable
存储元素和取出元素的位置是无序的
而LinkedHashMap存储元素和取出元素是一致的
15、红黑树
- 节点是红色或者黑色
- 根节点是黑色
- 每个叶子的节点都是黑色的空节点(NULL)
- 每个红色节点的两个子节点都是黑色的
- 从任意节点到其每个叶子的所有路径都包含相同的黑色节点