https://mp.weixin.qq.com/s/bVOSat47L0Hskfx9akAN6Q https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md
有序,可重复
参考: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/ArrayList%E6%BA%90%E7%A0%81+%E6%89%A9%E5%AE%B9%E6%9C%BA%E5%88%B6%E5%88%86%E6%9E%90.md https://mp.weixin.qq.com/s/WoGclm7SsbURGigI3Mwr3w
查询效率高,遍历效率高,增删效率低(需要大量移动元素),线程不安全
底层结构 Object[]数组以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。
每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)!
无序,不重复
Key-Value映射
线程不安全,使用key,value结构存储数据
JDK1.7:数组(初始容量默认为16)+链表 JDK1.8:数组(初始容量默认为16)+链表+红黑树(默认当链表长度达到8的时候会转换成红黑树)
默认0.75 加载因子越小,容易扩容,浪费空间,但查找效率高 链表特别短 减少hash冲突 加载因子越大,不易扩容,对空间的利用更加充分,查找效率低(链表拉长)hash冲突比较多,链表比较长
JDK1.7:采用头插入法,会改变链表的顺序,在多线程操作的情况下,可能出现环形链表,造成死锁 JDK1.8:采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。
首先看下index的计算公式:index = HashCode(Key) & (Length- 1)
参考: https://mp.weixin.qq.com/s/AHWzboztt53ZfFZmsSnMSw https://mp.weixin.qq.com/s/AixdbEiXf3KfE724kg2YIw
线程安全
JDK1.7 使用分段锁,使用CAS+Unsafe+ReentrantLock+Volatile,Segment 继承了 ReentrantLock
JDK1.8 没有分段锁,使用CAS+Unsafe+Synchronized+Volatile
JDK1.7 Segment 数组 + HashEntry 数组 + 链表,每个 Segment 里边是由 HashEntry 组成的数组,每个 HashEntry之间又可以形成链表。我们可以把每个 Segment 看成是一个小的 HashMap,其内部结构和 HashMap 是一模一样的。所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。
JDK1.8 Node 数组 + 链表 / 红黑树
线程安全,get 和put 都会用 synchronized 关键字加锁。 Hashtable 初始容量为:11,负载因子默认都是:0.75。 Hashtable 扩容规则为当前容量翻倍 + 1。
红黑树(自平衡的排序二叉树),有序
有序的HashMap