一文吃透 Java 集合全家桶:底层原理、扩容机制、并发问题、实战选型全覆盖 📅 2026/6/29 18:27:44 前言Java 集合是 Java 基础当中最核心的内容几乎所有业务开发都会高频使用同时也是校招、社招面试必问的重难点。很多开发者日常只会调用add()、get()方法不了解底层数据结构、扩容阈值、哈希冲突解决办法以及线程安全问题写代码时随意选用集合最终出现扩容耗时、死循环、数据丢失、并发脏读等线上问题。本篇文章完整拆解 Collection 单列集合与 Map 双列集合全部实现类从底层源码、初始化参数、扩容规则、红黑树转换、并发缺陷、业务选型六个维度进行讲解零基础也可以彻底吃透集合。一、Java 集合整体架构Java 集合框架分为两大根接口二者互不继承分工明确Collection 单列集合一次存放一个对象父接口下分为 List、Set、Queue 三个子接口Map 双列集合存储键值对key‑valuekey 唯一value 可以重复1.1 Collection 继承体系Collection ├─ List 有序、可重复、支持下标索引 │ ├─ ArrayList │ ├─ LinkedList │ └─ Vector ├─ Set 无序、元素不可重复 │ ├─ HashSet │ ├─ LinkedHashSet │ └─ TreeSet └─ Queue 队列先进先出阻塞队列 ├─ ArrayDeque └─ PriorityQueue1.2 Map 继承体系Map ├─ HashMap ├─ LinkedHashMap ├─ TreeMap ├─ Hashtable └─ ConcurrentHashMap二、List 集合深度解析List 最大特点有序、允许重复元素、支持通过索引下标直接访问元素。2.1 ArrayList底层结构底层为动态 Object 数组默认初始容量为 10。扩容机制当数组元素数量达到数组长度的 0.75 倍负载因子 0.75触发扩容扩容新容量 原容量 × 1.5扩容底层会新建一个更大的数组将原有数组元素拷贝到新数组中数组拷贝耗时较长。优缺点优点随机下标查询时间复杂度 O (1)遍历速度快尾部新增元素效率高。缺点数组中间位置新增、删除元素需要整体移动后续元素效率极低。关键细节底层数组会预留空位置size 记录实际元素个数length 代表数组总容量初始容量可以手动指定大批量插入数据时建议初始化指定容量减少多次扩容。2.2 LinkedList底层结构JDK1.8 底层是双向链表每一个节点 Node 内部保存前驱节点、后继节点、当前元素。增删查询特点头部、尾部新增 / 删除元素时间复杂度 O (1)根据下标查询元素需要从头节点或者尾节点开始遍历查找时间复杂度 O (n)。常用 APILinkedList 既可以当作 List 使用也可以当作栈、队列使用提供offer()、poll()、peek()、pop()等队列方法。2.3 VectorVector 底层同样是动态数组和 ArrayList 高度相似。 区别Vector 几乎所有方法都添加synchronized同步锁线程安全但是锁竞争带来巨大性能损耗现在开发基本废弃多线程场景优先选择 CopyOnWriteArrayList。2.4 List 选型总结查询多、随机下标访问多、尾部新增多 → ArrayList频繁头部 / 中间位置增删 → LinkedList多线程读写场景 → CopyOnWriteArrayList三、Set 集合完整讲解Set 集合核心特性元素不可重复存取无序TreeSet、LinkedHashSet 除外。 所有 Set 实现类底层全部依托 Map 实现存入 Set 的元素作为 Map 的 keyvalue 统一填充一个固定的空对象。3.1 HashSet底层HashMap 数组 链表 红黑树。存入元素先计算哈希值定位数组下标下标位置没有元素直接存放下标位置存在元素调用 equals 方法比较内容内容一致直接舍弃实现去重负载因子 0.75扩容规则和 HashMap 一致。3.2 LinkedHashSet继承自 HashSet底层为 LinkedHashMap。 特点哈希表 双向链表可以保证元素存入顺序与取出顺序一致去重同时保留插入顺序。3.3 TreeSet底层是 TreeMap基于红黑树实现。元素按照自然排序规则自动完成排序自定义对象存入 TreeSet 必须实现 Comparable 接口或者构造器传入比较器 Comparator底层依靠 compareTo 方法判断元素是否重复不再使用 equals 方法。四、Map 集合核心源码深度拆解Map 用来存储键值对key 不允许重复value 可以重复是业务开发最常用的集合。4.1 HashMap面试高频考点JDK1.8 底层结构数组 单向链表 红黑树。数组是哈希表主体每一个桶位存放链表头节点哈希冲突多个元素哈希值计算出同一个下标形成链表链表长度大于等于 8并且数组长度大于 64链表转为红黑树红黑树节点数量小于 6红黑树退化为链表。扩容机制默认初始容量 16负载因子 0.75扩容阈值 数组长度 × 负载因子扩容之后数组长度翻倍元素重新计算哈希值完成 rehash 迁移。关键特性key 允许为 nullvalue 允许为 null存取无序不保证插入顺序线程不安全多线程 put 操作容易出现死循环、数据丢失。4.2 LinkedHashMap底层继承 HashMap额外新增双向链表维护存取顺序。 分为两种模式插入有序模式按照存入顺序遍历LRU 淘汰模式最近最少使用访问过的节点放到链表尾部适合实现缓存淘汰策略。4.3 TreeMap底层红黑树key 按照自然排序。 存入自定义对象必须实现比较器key 不能为 null存取结果按照 key 升序排列。4.4 Hashtable底层数组 链表没有红黑树优化方法全部加 synchronized线程安全key 和 value 都不允许为 null初始容量 11扩容规则为 2 倍 1性能较差基本被 ConcurrentHashMap 替代。4.5 ConcurrentHashMap高并发场景首选JDK1.7 版本分段锁 Segment每一个 Segment 独立加锁多线程可以同时操作不同分段提高并发度。JDK1.8 版本取消分段锁底层结构和 HashMap 一致数组 链表 红黑树。 加锁方式CAS 无锁操作 synchronized 锁头节点。初始化扩容时使用 CAS 自旋保证安全写入数据时只锁住当前桶位头节点不阻塞其他桶位的写入操作并发性能极高。特性key、value 都不允许 null杜绝空指针歧义线程安全。五、Queue 队列集合5.1 ArrayDeque底层循环数组没有容量限制双向队列可以当作栈、双端队列使用不允许存放 null 元素。5.2 PriorityQueue底层小顶堆队列内部元素按照优先级自动排序队首永远是优先级最高的元素。六、集合高频面试问题HashMap 扩容为什么是 2 倍扩容 数组长度扩容为 2 的幂次可以让 hash 取模运算直接替换为位运算提升计算效率同时 rehash 迁移元素时只需要判断高位二进制位迁移效率更高。HashMap 链表转红黑树阈值为什么是 8 链表查询时间复杂度 O (n)红黑树查询 O (logn)。链表长度较短时链表遍历速度优于红黑树链表过长红黑树优势凸显。8 是统计得出的最优平衡点。HashMap 为什么负载因子设置为 0.75 负载因子过小频繁扩容空间浪费负载因子过大哈希冲突急剧增多链表变长查询效率下降。0.75 是空间利用率与哈希冲突的最优平衡值。HashMap 与 ConcurrentHashMap 区别 HashMap 线程不安全多线程会造成死循环、数据丢失ConcurrentHashMap 底层 CASsynchronized 保证线程安全高并发下性能优秀。重写 equals 方法为什么一定要重写 hashCode HashSet、HashMap 依靠 hashCode 计算存放下标如果两个对象 equals 相等hashCode 返回值不一样就会存入不同桶位破坏集合去重规则。七、开发实战集合选型方案普通单机查询多 → ArrayList频繁头尾增删 → LinkedList需要无序去重 → HashSet需要有序去重 → LinkedHashSet需要元素自动排序 → TreeSet普通键值对存储 → HashMap需要保证插入顺序 → LinkedHashMap按键排序存储 → TreeMap高并发读写键值对 → ConcurrentHashMap多线程 List 读写 → CopyOnWriteArrayList八、集合使用避坑指南大批量新增数据时提前指定集合初始容量避免多次扩容带来的性能损耗不要在遍历集合的过程中直接调用集合 remove 方法会抛出并发修改异常推荐使用迭代器删除自定义实体类存入 HashMap、HashSet必须重写 equals 和 hashCode 方法禁止在多线程环境下直接使用 HashMap高并发场景一定选用 ConcurrentHashMapTreeSet、TreeMap 存入对象必须实现比较规则否则会抛出类型转换异常。