知识梳理

it2023-05-01  76

结构

参考

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

List

有序,可重复

ArrayList

参考: 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 倍左右)!

新增和删除使用的是Copy

Vector

特点 线程安全,效率低底层结构 Object[]数组扩容 每次扩容之后容量都会变为原来的 2倍左右

LinkedList

特点 查询效率低,遍历效率低,增删效率高,线程不安全底层结构 双向链表

Set

无序,不重复

HashSet

特点 添加速度快,查询速度快,删除速度快,无序底层结构 采用 Hashmap 的 key 来储存元素; 数值放在 map 中的 key 上,value 上放了个 PRESENT,是一个静态的 Object,相当于 place holder,每个 key 都指向这个 object。

TreeSet

特点 有序,查询速度(按照内容查询)比List快,查询速度没有HashSet快底层结构 红黑树

LinkedHashSet

特点 有序底层结构 HashSet + LinkedList 的结构

Map

Key-Value映射

HashMap

特点

线程不安全,使用key,value结构存储数据

底层结构

JDK1.7:数组(初始容量默认为16)+链表 JDK1.8:数组(初始容量默认为16)+链表+红黑树(默认当链表长度达到8的时候会转换成红黑树)

JDK1.8 PUT过程

加载因子

默认0.75 加载因子越小,容易扩容,浪费空间,但查找效率高 链表特别短 减少hash冲突 加载因子越大,不易扩容,对空间的利用更加充分,查找效率低(链表拉长)hash冲突比较多,链表比较长

扩容

什么时候扩容 当 现有容量 >总容量 × 负载因子 的时候进行扩容 例如:给定的默认容量为 16,加载因子为 0.75。Map 在使用过程中不断的往里面存放数据,当数量达到了 16 * 0.75 = 12 就需要将当前 16 的容量进行扩容如何扩容 分为两步 扩容:创建一个新的Entry空数组,长度是原数组的2倍。 ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。

扩容时死循环问题

JDK1.7:采用头插入法,会改变链表的顺序,在多线程操作的情况下,可能出现环形链表,造成死锁 JDK1.8:采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

为什么初始容量是2的n次幂

首先看下index的计算公式:index = HashCode(Key) & (Length- 1)

ConcurrentHashMap

参考: 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 数组 + 链表 / 红黑树

PUT操作

JDK1.7 JDK1.8

HashTable

线程安全,get 和put 都会用 synchronized 关键字加锁。 Hashtable 初始容量为:11,负载因子默认都是:0.75。 Hashtable 扩容规则为当前容量翻倍 + 1。

TreeMap

红黑树(自平衡的排序二叉树),有序

LinkedHashMap

有序的HashMap

最新回复(0)