HashMap
- get和put的时间复杂度为O(1)
- 为了解决hash冲突,HashMap引入了链表
- 计算数组下标并不是用取模,而是用位运算 hashcode&(length-1),位运算效率>取模,因为位运算是最接近于机器所能识别的运算,cpu二进制
- hashmap默认扩容因子0.75,扩容阈值:16*0.75,最大容量2的30次方, 初始容量2的4次方即16
- 默认初始容量16,如果自定义初始容量必须是2的指数幂,如果不是,则强行转换为大于这个数的2的最小指数幂。
- HashMap是线程不安全的,不安全的具体原因就是在高并发场景下,扩容可能产生死锁(Jdk1.7存在)以及get操作可能带来的数据丢失。
为什么要转换成2的指数幂?
减少哈希碰撞,为了让哈希结果更加散列。hashcode&(length-1),hashcode会进行一些异或运算(扰动计算),让哈希散布的更加均匀,尽量减少哈希碰撞。
hashcode&(length-1)?
长度为什么要减1,不减1的话&操作之后只有两种结果,要么为1要么为0。
扩容以后旧数组往新数组迁移会有问题?
多线程下链表成死环的问题
1.8jdk的hashmap为什么链表长度>=8转换为红黑树,为什么是8呢?
长度为8时
JDK1.7
- 底层实现:数组+链表,数组作用:时间复杂度O(1),链表作用:解决哈希冲突
- 头插法,整体下移
JDK1.8
- 引入红黑树,链表长度>8且数组长度>=64转换为红黑树,否则优先扩容。为什么要把长的链表转换成红黑树呢,因为插入操作在查找的过程中用了一个 for 循环找到尾部,时间复杂度是O(n),而红黑树可以让搜索的复杂度降到O(logn)
- 尾插法(解决1.7下多线程插入死循环的问题),高低位指针。