摘要:Java 集合框架(Java Collections Framework)是每一位 Java 开发者必须跨越的门槛。无论是日常业务开发,还是应对大厂面试,对集合的理解深度往往决定了代码的质量和职业的高度。本文将基于 Java 核心体系,从 List、Set 到 Map,深度剖析 ArrayList、LinkedList、HashMap、ConcurrentHashMap 等核心组件的底层实现、扩容机制及线程安全策略,助你构建完整的集合知识体系。
在 Java 中,集合主要分为两大阵营:
List:有序,可重复。
Set:无序,不可重复。
Queue:队列,遵循 FIFO(先进先出)原则(本文暂略,重点聚焦图片内容)。

List 接口是 Collection 的子接口,其最大特点是有序(Ordered)且可重复(Allow Duplicates)。用户可以通过整数索引(Index)精确控制元素的插入位置和访问指定元素。
ArrayList 是 Java 中最常用的集合类,没有之一。
正如图片中所述,ArrayList 的底层是容量可变的对象数组 (Object[] elementData)。
很多初学者知道 ArrayList 会扩容,但不知道具体细节。
初始容量:当创建一个空的 ArrayList 时,默认的空数组。当添加第一个元素时,容量会初始化为 10。
扩容触发:当当前数组填满时(即 size == capacity),再次添加元素就会触发扩容。
扩容逻辑:
在 JDK 1.8 中,扩容的核心代码在 grow() 方法中。
int newCapacity = oldCapacity + (oldCapacity >> 1);oldCapacity >> 1 相当于除以 2。因此,新容量大约是旧容量的 1.5 倍。
数据迁移:扩容不仅仅是改变数值,更重要的是数据拷贝。ArrayList 底层调用 System.arraycopy 或 Arrays.copyOf 将原数组的数据复制到新的大数组中。
⚠ 性能警示:由于数组扩容涉及内存申请和数据复制,如果数据量巨大,频繁扩容会严重影响性能。最佳实践是在创建
ArrayList时,如果能预估数据量,最好指定初始容量,如new ArrayList<>(1000)。
ArrayList 是非线程安全的。在多线程环境下并发写入可能导致 ConcurrentModificationException 或数据丢失。
LinkedList 本质上是一个双向链表(Doubly Linked List)。每个节点(Node)包含三个部分:
item: 存储数据。
next: 指向下一个节点的指针。
prev: 指向上一个节点的指针。
remove(5)),它首先需要遍历找到第 5 个节点(O(n/2)),然后再删除。
LinkedList 做了一点优化,如果索引小于 size 的一半,从头遍历;否则从尾遍历。
特性 | ArrayList | LinkedList |
|---|---|---|
底层实现 | 动态数组 | 双向链表 |
随机访问 | O(1) (快) | O(n) (慢) |
插入/删除 | O(n) (慢,需移动元素) | O(1) (快,仅改指针,前提是持有节点) |
内存占用 | 较低(紧凑) | 较高(每个元素需额外存储两个指针) |
应用场景 | 读多写少,尾部追加 | 频繁在中间插入/删除 |
ArrayList 的前身,底层也是数组,但它所有的方法都加了 synchronized 关键字。这意味着它是线程安全的,但性能极差。现在几乎不再使用,多线程场景推荐使用 CopyOnWriteArrayList。
Vector,实现了栈(LIFO)结构。由于继承了 Vector 的笨重,现在推荐使用 ArrayDeque 来实现栈的功能。
Set 接口的核心契约是:不允许存在重复的元素。
大多数人不知道,HashSet 的源码极其简单,简单到甚至有点“偷懒”。
HashSet 的底层完全基于 HashMap 实现。
HashSet.add(E e) 时,实际上是将元素 e 作为 Key 存入了内部的 HashMap 中。
PRESENT 的静态 Object 常量(虚拟值)。
// HashSet 源码示意
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}因为 HashMap 的 Key 是唯一的,所以 HashSet 的元素自然也是唯一的。这依赖于两个方法:
hashCode(): 计算哈希值,确定存储位置。
equals(): 如果哈希值冲突,比较内容是否相同。
面试考点:如果将自定义对象存入 HashSet,必须重写
hashCode()和equals(),否则会出现逻辑上相同的对象被视为不同元素的情况。
HashSet 是无序的,但有时我们需要保持元素的插入顺序。这时 LinkedHashSet 就派上用场了。
HashSet,但其内部使用的是 LinkedHashMap。
LinkedHashSet 时,输出顺序与插入顺序一致。
TreeSet 的底层是 TreeMap。
Comparable 接口。
TreeSet 时传入一个 Comparator 比较器。
Map 是 Java 中最复杂的集合,也是面试中含金量最高的部分。Map 存储 Key-Value 映射,Key 无序且唯一,Value 可重复。
HashMap 是 Java 程序员必须精通的类。
图片中提到了这个关键点,我们来展开讲讲:
(n-1) & hash 高效计算索引)。
LinkedHashMap 继承自 HashMap。
accessOrder = true。每当读取一个 Key,该 Key 就会被移到链表尾部。这是实现 LRU(Least Recently Used,最近最少使用)缓存算法的核心机制。
TreeMap 基于红黑树实现。
Hashtable(注意 t 是小写)是 JDK 1.0 就存在的类。
synchronized)。
ConcurrentHashMap 取代。
这是多线程环境下的首选 Map。
Segment 数组,每个 Segment 本质上是一个小的 HashTable(继承自 ReentrantLock)。
Node 数组。
synchronized 锁住当前桶的头节点(链表头或树根)。
Java 集合框架博大精深,掌握它们不仅仅是背诵 API,更重要的是理解其背后的设计哲学:
java.util.concurrent 包下的 ConcurrentHashMap、CopyOnWriteArrayList 等并发容器。
希望这篇详解能帮助你从“会用”进阶到“懂原理”,在面试和实战中游刃有余!