学习记录 3/24
HashMap
- 默认容量是16,负载因子是0.75,拉链法解决hash冲突
- 如果初始化指定容量,会找到最接近的2的幂作为容量
- 当链表长度大于等于8时,且数组的长度大于等于64时,会将链表转化为红黑树,当链表长度小于等于6时,会将树退化为链表,如果数组小于64则会进行扩容,另外当元素个数达到容量*负载因子时也会触发扩容。当每次扩容都会扩容为原容量的2倍,即保证容量一定是2的幂。
- jdk1.7和jdk1.8中HashMap的实现区别:7只有链表,8会转化红黑树提高查询效率。7用头插法,8用尾插法,头插法在并发扩容时会导致死循环,并且头插法在扩容会导致链表逆序。7中扩容时rehash是重新计算hash和index,而8中进行了优化,因为数组大小一定是2的幂,所以用&运算代替取余,并且可以根据hash的最高位决定是在原来的桶中,还是在原来的桶位置+oldCap位置的桶中。
LinkedHashMap
- 继承HashMap,但是实现了三个方法来维护双向链表
- 使用双向链表按照插入顺序维护元素之间的连接
- 支持插入顺序遍历、访问顺序遍历
- 可以用来实现LRU缓存
- 与HashMap相比,维护了双向链表,可以有序遍历元素,且迭代效率更高,但是因为维护双向链表所以插入效率相较于HashMap略有下降。HashMap的遍历时无序的。
- 使用JMH对
HashMap和LinkedHashMap进行性能测试,总的来说均摊的性能差距并不是特别大
- 测试环境
CPU:12th Gen Intel(R) Core(TM) i5-12400
RAM:62G
- 存储一千万个简单元素
- HashMap:2199.965 ms/op
- LinkedHashMap:2075.718 ms/op
- 遍历一千万个简单元素
- HashMap:652.546 ms/op
- LinkedHashMap:303.675 ms/op
- LeetCode 31.下一个排列
- 考察双指针和字典序定义