集合
ArrayList
底层是动态数组,初始数组长度为0,add后默认为10,当需要增加的数组长度大于自身,会触发扩容,生成一个大于自身1.5倍的空数组,将原数组拷贝到新数组
根据下标查询,所以查询快,增删慢,修改需要移动其他数组位置
Vector
大部分方法被synchronized关键字修饰,线程安全的数组,扩容为2倍
LinkedList
底层是双向链表,头插LinkedFirst,尾插LinkedLast
链表是线性的数据存储,需要移动指针从前往后查找,所以查询慢;增删快,只用更改前后元素指针的指向,因为链表存储元素的同时,每个元素维护着前后指向的指针,但更占内存
HashMap
1.7是数组+单链表,1.8是数组+单链表+红黑树,链表和红黑树的转换,单链表长度大于等于8,并且hash桶长度大于等于64时,会将链表转换为红黑树,红黑树节点数据小于等于6时会转换为单链表
HashMap是非线程安全,多线程情况下1.7会产生循环链表,1.7是头插,t1将k2插入k1前时,t2发生resize,t2链表为k1-k2,t1链表为k2-k1;1.8改成尾插
扩容机制
hash桶默认是16,负载因子默认是0.75,阈值为16*0.75=12,hash桶占用容量超过12时会触发扩容,扩容成之前hash桶的2倍,2的N次幂,将之前的元素再进行一次hash运算,填充到新的hash桶中,按链表或红黑树的方式排列
HashMap的数组长度一定是2的次幂:
减少链表上元素的移动代价。因为按二次幂进行扩展,链表上元素的位置要么是在桶的原位置,要么在桶原位置再移动2次幂的位置
Hash冲突
- 开放地址法
关键字key出现冲突时,寻找下一个地址将key存起来 - 拉链法
将所有关键字相同的存在链表中 - rehash
再用另外一个哈希函数算出哈希值,直到算出不同值
ConcurrentHashMap
1.7底层是分段数组,为了保证线程安全有个Segment锁,每次只加一段锁保证并发度
1.8改成数组+链表+红黑树,会逐渐放弃这种分片锁机制,使用synchronized和CAS来操作