Java 集合框架是开发中最常用也最容易误用的部分。List、Set、Map 看似简单,其实背后有不少值得推敲的选择逻辑。今天我们从底层实现、使用语义、性能差异三个维度全面解析。
一、Java 集合框架总览
Java 的集合大致分为两大类:
-
Collection 接口族(单值集合):如 List、Set、Queue。
-
Map 接口族(键值对集合):如 HashMap、TreeMap。
它们均继承自 java.util 包中的核心接口,并基于不同的数据结构实现。
二、List:有序可重复的序列集合
常见实现:
实现类 | 底层结构 | 适用场景 |
---|---|---|
ArrayList | 数组 | 随机访问频繁、插入较少 |
LinkedList | 双向链表 | 插入删除频繁、迭代为主 |
CopyOnWriteArrayList | 数组 + 写时复制 | 并发读多写少场景 |
特性:
-
元素按插入顺序排列;
-
允许重复值;
-
可通过索引访问元素。
三、Set:去重 + 无序(或有序)的集合
常见实现:
实现类 | 底层结构 | 特点 |
---|---|---|
HashSet | HashMap 支持 | 插入快,无序,基于哈希去重 |
LinkedHashSet | HashMap + 链表 | 保证插入顺序 |
TreeSet | 红黑树(TreeMap) | 自动排序,元素需实现 Comparable |
特性:
-
不允许重复元素;
-
默认无序,可通过 TreeSet 实现排序。
四、Map:键值对集合,快速映射查找
常见实现:
实现类 | 底层结构 | 特点 |
---|---|---|
HashMap | 数组 + 链表/红黑树 | 查找快,键不可重复,线程不安全 |
LinkedHashMap | HashMap + 双向链表 | 保留插入顺序 |
TreeMap | 红黑树 | 自动按 key 排序,适合区间查找 |
ConcurrentHashMap | 分段锁 / CAS | 并发环境推荐,线程安全 |
特性:
-
每个 key 唯一,value 可重复;
-
可通过 key 快速定位 value;
-
TreeMap 可用于范围查询与有序 key 管理。
五、如何选?典型使用场景对比
需求场景 | 推荐集合 | 说明 |
---|---|---|
快速索引,允许重复 | List(ArrayList) | 下标访问 O(1),插入删除 O(n) |
去重 + 无序存储 | HashSet | 内部用 HashMap 实现 |
保持插入顺序 + 去重 | LinkedHashSet | 内部维护双向链表 |
自动排序 + 无重复 | TreeSet | 内部红黑树,有序输出 |
键值存储,查找频繁 | HashMap | key 必须实现 hashCode 与 equals |
保持插入顺序的键值映射 | LinkedHashMap | 可用于构建 LRU 缓存 |
线程安全键值对存储 | ConcurrentHashMap | 高并发推荐,读写分离提升性能 |
六、List vs Set vs Map:底层结构对比
接口 | 是否有序 | 是否允许重复 | 底层数据结构 |
---|---|---|---|
List | 有序 | ✅ | 数组 or 链表 |
Set | 无序/有序 | ❌ | Hash 表 / Tree |
Map | 有序/无序 | ✅(仅 value) | Hash 表 / Tree |
七、开发者常见误区
-
使用 List 存储去重数据 → 实际应使用 Set;
-
使用 HashSet 存储自定义对象却不重写 hashCode() 和 equals() → 无效去重;
-
使用 TreeMap 但 Key 未实现 Comparable 或未传 Comparator → 运行时异常;
-
在高并发场景下使用 HashMap 导致线程安全问题 → 应使用 ConcurrentHashMap。
八、各集合类源码核心方法解析
我们从最常用的三个集合类(ArrayList、HashSet、HashMap)出发,拆解其底层源码实现,理解其时间复杂度、扩容机制与数据结构本质。
🔹ArrayList 核心源码解析
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 自动扩容
elementData[size++] = e;
return true;
}
-
底层结构:动态数组 Object[] elementData;
-
扩容机制:容量不够时扩展为 oldCapacity + (oldCapacity >> 1)(即 1.5 倍);
-
时间复杂度:
-
随机访问:O(1)
-
插入末尾:O(1) 均摊
-
插入中间:O(n)
🔹HashSet 核心源码解析(基于 HashMap)
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
-
底层结构:使用 HashMap<E, Object> 实现;
-
唯一性保障:依赖 key 的 hashCode() + equals() 方法判断重复;
-
时间复杂度:O(1)(极端哈希冲突退化为 O(n));
🔹HashMap 核心源码解析(JDK 8+)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
1. 核心方法:putVal() 中包含:
-
计算 hash;
-
根据索引定位桶位;
-
冲突时使用链表(JDK 8+ 中链表超过阈值会转为红黑树);
2. 时间复杂度:
-
查找/插入:O(1),红黑树冲突为 O(log n)
九、集合类线程安全改造方案
在多线程场景中,Java 原生集合不是线程安全的,必须使用同步封装或并发安全类。
1. Collections.synchronizedXXX
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
-
优点:线程安全;
-
缺点:基于 synchronized,性能一般;
-
用于读写均衡、线程不太激烈的场景。
2. CopyOnWriteArrayList
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
-
读写分离:写操作复制新数组,读操作无需加锁;
-
缺点:写操作代价高,不适合频繁写;
-
用于读远多于写的并发场景(如配置缓存)。
3.ConcurrentHashMap
ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
-
JDK 8 之后基于 CAS + 分段锁 + 红黑树;
-
写入不阻塞读取,适合高并发读写;
-
不支持 null key 或 value。
总结选择建议:
场景 | 推荐集合 |
---|---|
低并发,简易同步 | Collections.synchronizedXXX |
读多写少 | CopyOnWriteArrayList / CopyOnWriteArraySet |
高并发读写 | ConcurrentHashMap / ConcurrentSkipListMap |
10、Java 21+ 新增集合特性与未来演进方向
随着 Java 的版本演进,集合框架也在不断增强功能与性能。
1. Record + 集合的简洁建模
Java 16+ 引入 record,搭配集合使用更加清晰:
record User(String name, int age) {}
List<User> users = List.of(new User("Alice", 30), new User("Bob", 25));
2. Map.of(), List.of() 的不可变集合
Java 9 引入的新 API,创建只读集合:
List<String> list = List.of("A", "B", "C"); // 不可修改
Map<String, Integer> map = Map.of("A", 1, "B", 2);
-
不可变特性有助于并发编程与函数式设计;
-
若尝试修改,会抛出 UnsupportedOperationException。
3. Java 21 的 Sequenced Collections(预览)
-
新接口:SequencedCollection、SequencedMap;
-
统一支持“前插、后插、正序、反序遍历”的操作;
-
实现类如:SequencedHashMap、SequencedSet;
SequencedSet<String> set = new LinkedHashSet<>();
set.addFirst("head");
set.addLast("tail");
4. 未来演进方向(根据 JEP 与 OpenJDK 讨论)
-
更好的不可变集合支持;
-
集合接口层级的模块化;
-
性能更高的并发集合类(如基于结构化并发与虚拟线程);
-
更贴近函数式风格的操作(如 map/filter 原生支持);