Java 四大核心集合(List/Set/Queue/Map)详解
一、Java 集合框架总览
Java 集合框架(Java Collections Framework, JCF)是用于存储、操作数据的核心 API,位于 java.util 包下,核心目标是 提供统一、高效的数据结构操作接口,替代数组的局限性(固定长度、操作繁琐)。
1.1 核心体系结构
Java 集合框架分为两大核心分支:
- Collection 接口:存储「单个元素」的集合,子接口包括
List(有序可重复)、Set(无序不可重复)、Queue(先进先出); - Map 接口:存储「键值对(Key-Value)」的集合,不继承 Collection,是独立的顶层接口。
1.2 核心接口关系表
| 顶层接口 | 核心子接口 / 实现类 | 存储类型 | 核心特征 |
|---|---|---|---|
Collection | List(ArrayList、LinkedList) | 单个元素 | 有序、可重复、有索引 |
Collection | Set(HashSet、LinkedHashSet、TreeSet) | 单个元素 | 无序、不可重复 |
Collection | Queue(ArrayDeque、PriorityQueue、BlockingQueue) | 单个元素 | 先进先出(FIFO)/ 优先级排序 |
Map | HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap | 键值对 | Key 唯一、Value 可重复 |
1.3 集合与数组的区别
| 特性 | 集合(如 List/Map) | 数组 |
|---|---|---|
| 长度 | 动态扩容(无需指定初始长度) | 固定长度(初始化后不可变) |
| 存储类型 | 只能存储引用类型(基本类型需装箱为包装类) | 可存储基本类型 / 引用类型 |
| 操作 API | 提供丰富方法(add/remove/get/contains 等) | 仅支持索引访问,无内置操作方法 |
| 灵活性 | 高(支持排序、遍历、查找、扩容) | 低(需手动实现排序、扩容等逻辑) |
二、List 详解:有序可重复的「动态数组」
2.1 核心特性
- 有序性:元素存储顺序与插入顺序一致,支持通过「索引」访问(0 起始);
- 可重复性:允许存储多个相等元素(
equals()返回 true); - 索引操作:支持通过索引增删改查(如
get(int index)、remove(int index)); - 非线程安全:默认实现(ArrayList、LinkedList)均为线程不安全,并发场景需手动同步。
2.2 常用实现类对比
| 实现类 | 底层结构 | 有序性 | 重复性 | 线程安全 | 查询效率 | 增删效率(中间 / 末尾) | 初始容量 | 扩容机制 |
|---|---|---|---|---|---|---|---|---|
ArrayList | 动态数组(Object []) | 插入有序 | 允许 | 否 | 高(O (1),索引直接访问) | 中间:低(O (n),需数组拷贝);末尾:高(O (1)) | 10(JDK8) | 扩容为原容量的 1.5 倍(oldCapacity + (oldCapacity >> 1)) |
LinkedList | 双向链表 | 插入有序 | 允许 | 否 | 低(O (n),需遍历链表) | 中间 / 末尾:高(O (1),仅需修改指针) | 无(链表无容量概念) | 无需扩容(链表节点动态新增) |
Vector | 动态数组(Object []) | 插入有序 | 允许 | 是(方法加 synchronized) | 高(O (1)) | 中间:低(O (n));末尾:高(O (1)) | 10 | 扩容为原容量的 2 倍 |
CopyOnWriteArrayList | CopyOnWrite 数组(写时复制) | 插入有序 | 允许 | 是(读写分离) | 高(O (1)) | 低(O (n),写操作需复制整个数组) | 0(初始为空数组) | 扩容为原容量 + 1(每次写操作新建数组) |
2.3 底层原理与关键机制
(1)ArrayList 核心机制
-
动态扩容
:当元素数量超过容量(
size >= capacity)时,触发扩容:- 计算新容量:
newCapacity = oldCapacity + (oldCapacity >> 1)(1.5 倍); - 若新容量仍不足(如手动添加大量元素),直接扩容为需求容量;
- 通过
Arrays.copyOf()复制原数组到新数组,开销较大。
- 计算新容量:
-
缩容:默认不支持自动缩容(即使删除大量元素,容量仍不变),需手动调用
trimToSize()释放空闲内存。
(2)LinkedList 核心机制
- 底层是「双向链表」,每个节点(
Node)包含prev(前驱指针)、item(元素)、next(后继指针); - 实现了
Deque接口,支持双端操作(如addFirst()、removeLast()),可作为队列 / 栈使用; - 无索引,查询需从表头 / 表尾遍历,效率低下。
(3)CopyOnWriteArrayList 核心机制
- 「写时复制」:读操作直接访问原数组(无锁,高效),写操作(add/remove)时复制原数组生成新数组,修改后替换原数组引用;
- 优点:读操作无并发冲突,无需加锁;缺点:写操作开销大,存在数据一致性问题(读可能获取旧数据)。
2.4 常用方法示例
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
// 1. ArrayList 示例(查询优先)
List<String> arrayList = new ArrayList<>();
arrayList.add("Java"); // 末尾添加:O(1)
arrayList.add("Python");
arrayList.add(1, "Golang"); // 中间插入:O(n),需数组拷贝
System.out.println("ArrayList 元素:" + arrayList); // [Java, Golang, Python]
System.out.println("索引 1 元素:" + arrayList.get(1)); // Golang(O(1))
arrayList.remove(2); // 移除索引 2 元素:O(n)
System.out.println("移除后:" + arrayList); // [Java, Golang]
// 2. LinkedList 示例(增删优先,双端操作)
LinkedList<String> linkedList = new LinkedList<>();
linkedList.addFirst("Redis"); // 表头添加:O(1)
linkedList.addLast("MySQL"); // 表尾添加:O(1)
linkedList.push("MongoDB"); // 栈操作(等价于 addFirst)
System.out.println("LinkedList 元素:" + linkedList); // [MongoDB, Redis, MySQL]
System.out.println("表头元素:" + linkedList.getFirst()); // MongoDB(O(1))
System.out.println("表尾元素:" + linkedList.getLast()); // MySQL(O(1))
linkedList.pop(); // 栈弹出(等价于 removeFirst)
System.out.println("弹出后:" + linkedList); // [Redis, MySQL]
}
}
2.5 线程安全问题与解决方案
(1)问题:
默认实现(ArrayList、LinkedList)在多线程并发读写时,可能出现 ConcurrentModificationException(快速失败)、数据错乱等问题。
(2)解决方案:
- 手动同步:使用
synchronized或ReentrantLock包裹集合操作; - 使用线程安全实现类:
- 低并发:
Vector(效率低,不推荐); - 高并发读、低并发写:
CopyOnWriteArrayList(读无锁,写复制);
- 低并发:
- 工具类包装:
Collections.synchronizedList(new ArrayList<>())(底层通过同步代码块实现,效率一般)。
2.6 适用场景
- ArrayList:查询频繁、增删少(如数据展示、配置列表);
- LinkedList:增删频繁、查询少(如队列、栈、链表结构数据);
- CopyOnWriteArrayList:高并发读场景(如缓存列表、日志收集);
- Vector:遗留系统兼容(不推荐新代码使用)。
三、Set 详解:无序不可重复的「集合」
3.1 核心特性
- 无序性:元素存储顺序与插入顺序无关(LinkedHashSet 除外,维护插入顺序);
- 不可重复性:不允许存储相等元素(通过
equals()和hashCode()判断); - 无索引:不支持通过索引访问,仅支持遍历、包含判断;
- 非线程安全:默认实现(HashSet、TreeSet)均为线程不安全。
3.2 常用实现类对比
| 实现类 | 底层结构 | 有序性 | 去重机制 | 线程安全 | 查找效率 | 插入效率 | 元素要求 |
|---|---|---|---|---|---|---|---|
HashSet | 哈希表(基于 HashMap,元素存于 Key 位) | 无序 | hashCode() + equals() | 否 | 高(O (1),哈希冲突时 O (n)) | 高(O (1)) | 需重写 hashCode() 和 equals() |
LinkedHashSet | 哈希表 + 双向链表(基于 LinkedHashMap) | 插入有序 | hashCode() + equals() | 否 | 高(O (1)) | 中(比 HashSet 多维护链表) | 需重写 hashCode() 和 equals() |
TreeSet | 红黑树(基于 TreeMap,元素存于 Key 位) | 自然排序 / 定制排序 | Comparable.compareTo() 或 Comparator | 否 | 中(O (log n),红黑树查找) | 中(O (log n)) | 元素需可比较(实现 Comparable 或传入 Comparator) |
CopyOnWriteArraySet | CopyOnWrite 数组(基于 CopyOnWriteArrayList) | 插入有序 | equals() | 是(读写分离) | 低(O (n),需遍历数组) | 低(O (n),写复制) | 需重写 equals() |
3.3 去重原理(核心!)
Set 的不可重复性依赖以下两个方法,必须同时满足:
hashCode():计算元素的哈希值,决定元素在哈希表中的存储位置(桶位);equals():判断两个元素是否逻辑相等。
去重流程(以 HashSet 为例):
- 新增元素时,先调用元素的
hashCode()得到哈希值,找到对应的桶位; - 若桶位为空,直接存入元素;
- 若桶位不为空,遍历桶内元素,通过
equals()对比:- 若存在
equals()返回 true 的元素,拒绝插入; - 若所有元素
equals()返回 false,存入桶内(哈希冲突,链表 / 红黑树存储)。
- 若存在
关键规范:
- 若
a.equals(b) = true,则a.hashCode()必须等于b.hashCode(); - 若
a.hashCode() = b.hashCode(),a.equals(b)可不为 true(哈希冲突); - 自定义对象作为 Set 元素时,必须重写
hashCode()和equals(),否则无法保证去重。
3.4 常用方法示例
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
public class SetDemo {
static class User {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
// 重写 equals() 和 hashCode()(关键:基于 id 去重)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
return "User{id='" + id + "', name='" + name + "'}";
}
}
public static void main(String[] args) {
// 1. HashSet(无序去重)
HashSet<User> hashSet = new HashSet<>();
hashSet.add(new User("1", "张三"));
hashSet.add(new User("2", "李四"));
hashSet.add(new User("1", "张三(重复)")); // 去重(id 相同)
System.out.println("HashSet 元素(无序):" + hashSet);
// 输出:[User{id='1', name='张三'}, User{id='2', name='李四'}]
// 2. LinkedHashSet(插入有序去重)
LinkedHashSet<User> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(new User("3", "王五"));
linkedHashSet.add(new User("4", "赵六"));
linkedHashSet.add(new User("3", "王五(重复)")); // 去重
System.out.println("LinkedHashSet 元素(插入有序):" + linkedHashSet);
// 输出:[User{id='3', name='王五'}, User{id='4', name='赵六'}]
// 3. TreeSet(自然排序,需实现 Comparable)
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
treeSet.add(3); // 去重
System.out.println("TreeSet 元素(自然排序):" + treeSet); // [1, 2, 3]
// 定制排序(按 User.name 长度排序)
TreeSet<User> customTreeSet = new TreeSet<>((u1, u2) -> u1.name.length() - u2.name.length());
customTreeSet.add(new User("5", "小明"));
customTreeSet.add(new User("6", "小红"));
customTreeSet.add(new User("7", "王大明"));
System.out.println("TreeSet 定制排序:" + customTreeSet);
// 输出:[User{id='5', name='小明'}, User{id='6', name='小红'}, User{id='7', name='王大明'}]
}
}
3.5 线程安全问题与解决方案
(1)问题:
默认实现(HashSet、TreeSet)并发读写时,可能出现 ConcurrentModificationException、数据重复插入等问题。
(2)解决方案:
- 工具类包装:
Collections.synchronizedSet(new HashSet<>()); - 并发安全实现类:
- 高并发读、低并发写:
CopyOnWriteArraySet(基于 CopyOnWriteArrayList,去重依赖equals()); - 高并发读写:
ConcurrentSkipListSet(基于跳表,有序,线程安全,效率高于 CopyOnWriteArraySet)。
- 高并发读、低并发写:
3.6 适用场景
- HashSet:无需有序、需快速去重(如用户 ID 集合、标签集合);
- LinkedHashSet:需有序去重(如历史记录、访问轨迹);
- TreeSet:需排序的去重集合(如排行榜、有序数据筛选);
- CopyOnWriteArraySet:高并发读场景的去重集合(如并发日志去重)。
四、Queue 详解:先进先出的「队列」
4.1 核心特性
- FIFO 原则:默认先进先出(PriorityQueue 除外,按优先级排序);
- 队列操作:支持「插入、移除、查看」三类核心操作,每种操作提供两种实现(失败抛异常 / 返回特殊值);
- 双端队列:部分实现(如 LinkedList、ArrayDeque)支持双端操作(头 / 尾均可插入 / 移除),可作为栈使用;
- 阻塞队列:支持阻塞等待(无元素时移除阻塞,无空间时插入阻塞),适用于并发场景。
4.2 核心方法对比(Queue 接口)
| 操作类型 | 失败抛异常 | 失败返回特殊值 | 说明 |
|---|---|---|---|
| 插入元素 | add(E e) | offer(E e) | add 超出容量抛 IllegalStateException,offer 返回 false |
| 移除元素 | remove() | poll() | remove 无元素抛 NoSuchElementException,poll 返回 null |
| 查看队首元素 | element() | peek() | element 无元素抛 NoSuchElementException,peek 返回 null |
4.3 常用实现类对比
| 实现类 | 底层结构 | 队列类型 | 有序性 | 线程安全 | 容量限制 | 核心特点 |
|---|---|---|---|---|---|---|
LinkedList | 双向链表 | 双端队列(Deque) | FIFO | 否 | 无(动态扩容) | 支持双端操作,可作为队列 / 栈使用 |
ArrayDeque | 循环数组 | 双端队列(Deque) | FIFO | 否 | 无(动态扩容) | 数组实现,效率高于 LinkedList,支持栈操作(push/pop) |
PriorityQueue | 小顶堆(数组实现) | 优先级队列 | 自然排序 / 定制排序 | 否 | 无(动态扩容) | 不遵循 FIFO,按优先级取出元素(默认小顶堆) |
ConcurrentLinkedQueue | 单向链表 | 并发队列 | FIFO | 是(CAS 无锁) | 无 | 高并发场景下高效,非阻塞 |
ArrayBlockingQueue | 数组 | 阻塞队列(BlockingQueue) | FIFO | 是(ReentrantLock) | 有界(需指定容量) | 数组实现,公平 / 非公平锁可选 |
LinkedBlockingQueue | 双向链表 | 阻塞队列(BlockingQueue) | FIFO | 是(ReentrantLock) | 可选有界(默认无界) | 链表实现,吞吐量高于 ArrayBlockingQueue |
SynchronousQueue | 无存储结构 | 同步队列 | FIFO | 是 | 无界(但每次只能存 1 个元素) | 插入操作需等待移除操作,适用于线程间数据传递 |
4.4 底层原理与关键机制
(1)PriorityQueue 核心机制
- 底层是「小顶堆」(完全二叉树的数组实现),默认按自然排序(元素需实现
Comparable); - 插入元素时,通过「上浮」调整堆结构(确保父节点小于子节点);
- 移除队首元素时,通过「下沉」调整堆结构(确保堆顶是最小值);
- 注意:PriorityQueue 不是线程安全的,并发场景需使用
PriorityBlockingQueue。
(2)BlockingQueue 核心机制
- 基于「锁 + 条件变量」实现阻塞:
put(E e):队列满时阻塞,直到有空间;take():队列空时阻塞,直到有元素;- 支持超时阻塞(
offer(E e, long timeout, TimeUnit unit)、poll(long timeout, TimeUnit unit));
- 适用于生产者 - 消费者模型(生产者插入元素,消费者移除元素,自动协调并发)。
4.5 常用方法示例
import java.util.PriorityQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class QueueDemo {
public static void main(String[] args) throws InterruptedException {
// 1. PriorityQueue(优先级队列,小顶堆)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(3);
priorityQueue.offer(1);
priorityQueue.offer(2);
System.out.println("PriorityQueue 队首:" + priorityQueue.peek()); // 1(最小值)
while (!priorityQueue.isEmpty()) {
System.out.print(priorityQueue.poll() + " "); // 1 2 3(按优先级取出)
}
System.out.println();
// 2. ArrayBlockingQueue(有界阻塞队列,生产者-消费者示例)
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2); // 容量 2
// 生产者线程
new Thread(() -> {
try {
blockingQueue.put("任务1");
System.out.println("生产者:放入任务1");
blockingQueue.put("任务2");
System.out.println("生产者:放入任务2");
blockingQueue.put("任务3"); // 队列满,阻塞
System.out.println("生产者:放入任务3");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer").start();
// 消费者线程
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1); // 休眠 1 秒,模拟处理时间
System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务1,队列有空间,生产者继续放入任务3
TimeUnit.SECONDS.sleep(1);
System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务2
TimeUnit.SECONDS.sleep(1);
System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务3
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Consumer").start();
}
}
4.6 适用场景
- LinkedList/ArrayDeque:普通队列 / 栈操作(如任务临时存储、栈结构计算);
- PriorityQueue:优先级任务调度(如任务优先级排序、Top K 问题);
- ConcurrentLinkedQueue:高并发非阻塞队列(如日志收集、无阻塞任务队列);
- ArrayBlockingQueue/LinkedBlockingQueue:生产者 - 消费者模型(如线程池任务队列、消息队列);
- SynchronousQueue:线程间同步通信(如线程池的
SynchronousQueue用于直接提交任务)。
五、Map 详解:键值对映射的「字典」
5.1 核心特性
- 键值对存储:每个元素是
Entry<K, V>对象(Key-Value 映射); - Key 唯一性:Key 不可重复(基于
hashCode()和equals()判断),Value 可重复; - 无序性:默认实现(HashMap)无序,LinkedHashMap 维护插入顺序,TreeMap 维护排序顺序;
- 非线程安全:默认实现(HashMap、LinkedHashMap)均为线程不安全。
5.2 常用实现类对比
| 实现类 | 底层结构 | 有序性 | Key 唯一性 | 线程安全 | 查找效率 | 插入效率 | Key/Value 限制 |
|---|---|---|---|---|---|---|---|
HashMap | JDK7:数组 + 链表;JDK8:数组 + 链表 / 红黑树 | 无序 | hashCode() + equals() | 否 | 高(O (1),冲突时 O (log n)) | 高(O (1)) | Key 可 null(仅 1 个),Value 可 null |
LinkedHashMap | 哈希表 + 双向链表 | 插入有序 / 访问有序 | hashCode() + equals() | 否 | 高(O (1)) | 中(比 HashMap 多维护链表) | Key 可 null,Value 可 null |
TreeMap | 红黑树 | 自然排序 / 定制排序 | Comparable.compareTo() 或 Comparator | 否 | 中(O (log n)) | 中(O (log n)) | Key 不可 null,Value 可 null |
Hashtable | 数组 + 链表 | 无序 | hashCode() + equals() | 是(方法加 synchronized) | 低(O (1),冲突时 O (n)) | 低 | Key/Value 均不可 null |
ConcurrentHashMap | JDK7:分段锁;JDK8:数组 + 链表 / 红黑树 + CAS+synchronized | 无序 | hashCode() + equals() | 是(并发安全) | 高(接近 HashMap) | 高(接近 HashMap) | Key/Value 均不可 null |
5.3 底层原理与关键机制
(1)HashMap 核心机制(JDK8)
- 底层结构:数组(桶)+ 链表 + 红黑树(哈希冲突解决);
- 数组:存储
Entry对象,初始容量 16(2^4),必须是 2 的幂(便于哈希计算); - 链表:哈希冲突时,元素链化(链表长度 <= 8 时);
- 红黑树:链表长度 >= 8 且数组长度 >= 64 时,链表转为红黑树(提升查询效率)。
- 数组:存储
- 哈希计算:
index = (n - 1) & hash(n 为数组长度,保证 index 在数组范围内); - 负载因子:默认 0.75(平衡空间与时间),当
size > capacity * loadFactor时触发扩容; - 扩容机制:扩容为原容量的 2 倍,重新计算所有元素的哈希值并迁移(rehash)。
(2)ConcurrentHashMap 核心机制(JDK8)
- 放弃 JDK7 的「分段锁」,采用「CAS + synchronized」实现并发安全:
- 对数组桶的头节点加
synchronized锁,减少锁竞争(粒度更细); - 读操作无锁(volatile 保证可见性);
- 支持高并发读写,吞吐量远高于 Hashtable。
- 对数组桶的头节点加
(3)LinkedHashMap 核心机制
- 基于 HashMap,额外维护一条「双向链表」,记录元素的插入顺序或访问顺序;
- 可通过构造函数指定访问顺序(
accessOrder = true),适用于 LRU 缓存(最近最少使用淘汰)。
5.4 常用方法示例
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class MapDemo {
public static void main(String[] args) {
// 1. HashMap(无序,Key 唯一)
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Java", 95);
hashMap.put("Python", 85);
hashMap.put("Java", 100); // Key 重复,覆盖 Value
System.out.println("HashMap:" + hashMap); // {Java=100, Python=85}(无序)
System.out.println("获取 Java 分数:" + hashMap.get("Java")); // 100
System.out.println("是否包含 Key Python:" + hashMap.containsKey("Python")); // true
// 2. LinkedHashMap(插入有序)
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 95);
linkedHashMap.put("Python", 85);
linkedHashMap.put("Golang", 90);
System.out.println("LinkedHashMap(插入有序):" + linkedHashMap);
// 输出:{Java=95, Python=85, Golang=90}
// 3. TreeMap(自然排序)
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("C", 80);
treeMap.put("A", 90);
treeMap.put("B", 85);
System.out.println("TreeMap(自然排序):" + treeMap); // {A=90, B=85, C=80}
// 4. ConcurrentHashMap(并发安全)
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 多线程并发操作(无异常)
new Thread(() -> concurrentHashMap.put("线程1", 1)).start();
new Thread(() -> concurrentHashMap.put("线程2", 2)).start();
new Thread(() -> System.out.println("ConcurrentHashMap:" + concurrentHashMap)).start();
}
}
5.5 线程安全问题与解决方案
(1)问题:
默认实现(HashMap、LinkedHashMap)并发读写时,可能出现 ConcurrentModificationException、数据错乱、扩容死循环(JDK7)等问题。
(2)解决方案:
- 工具类包装:
Collections.synchronizedMap(new HashMap<>())(底层同步代码块,效率一般); - 线程安全实现类:
- 遗留系统:
Hashtable(效率低,不推荐); - 高并发场景:
ConcurrentHashMap(JDK8 效率高,推荐); - 有序并发:
ConcurrentSkipListMap(基于跳表,有序,并发安全)。
- 遗留系统:
5.6 适用场景
- HashMap:普通键值对映射(如配置存储、缓存、字典查询);
- LinkedHashMap:有序键值对(如历史记录、LRU 缓存);
- TreeMap:有序键值对排序(如排行榜、区间查询);
- ConcurrentHashMap:高并发键值对映射(如分布式缓存、并发配置存储);
- ConcurrentSkipListMap:高并发有序键值对(如并发排行榜)。
六、四大集合核心对比总结
| 集合类型 | 核心特征 | 有序性 | 重复性 | 线程安全(默认) | 常用实现类 | 核心用途 |
|---|---|---|---|---|---|---|
List | 索引访问 | 插入有序 | 允许 | 否 | ArrayList、LinkedList | 动态数组、有序列表 |
Set | 不可重复 | 无序(LinkedHashSet 除外) | 禁止 | 否 | HashSet、TreeSet | 去重、集合运算 |
Queue | FIFO / 优先级 | FIFO(PriorityQueue 除外) | 允许 | 否 | ArrayDeque、BlockingQueue | 队列、任务调度 |
Map | 键值对 | 无序(LinkedHashSet/TreeMap 除外) | Key 禁止、Value 允许 | 否 | HashMap、ConcurrentHashMap | 字典映射、缓存 |
七、常见问题与注意事项
7.1 equals () 与 hashCode () 重写规范
- 适用场景:Set 元素、Map Key 为自定义对象时;
- 规范:
- 若
a.equals(b) = true,则a.hashCode() = b.hashCode(); - 若
a.hashCode() != b.hashCode(),则a.equals(b) = false; - 重写
equals()必须重写hashCode(),反之不必然;
- 若
- 示例:基于对象的核心字段(如 ID)重写,避免使用默认的
Object类实现(基于内存地址)。
7.2 fail-fast 与 fail-safe 机制
- fail-fast(快速失败):
- 触发场景:ArrayList、HashMap 等集合在迭代时,若其他线程修改集合(add/remove),会抛出
ConcurrentModificationException; - 原理:迭代器维护
modCount(修改次数),迭代时检查modCount是否变化; - 注意:单线程迭代时,若通过集合自身方法(而非迭代器)修改集合,也会触发。
- 触发场景:ArrayList、HashMap 等集合在迭代时,若其他线程修改集合(add/remove),会抛出
- fail-safe(安全失败):
- 触发场景:CopyOnWriteArrayList、ConcurrentHashMap 等并发集合;
- 原理:迭代时访问集合的「快照」(如 CopyOnWrite 数组的原数组),修改操作不影响迭代;
- 缺点:可能读取到旧数据,写操作开销大。
7.3 集合选型建议
- 需有序、可重复、索引访问 →
List:- 查询多 → ArrayList;
- 增删多 → LinkedList;
- 并发读 → CopyOnWriteArrayList。
- 需去重 →
Set:- 无序 → HashSet;
- 有序 → LinkedHashSet;
- 排序 → TreeSet;
- 并发 → CopyOnWriteArraySet。
- 需队列 / 栈操作 →
Queue:- 普通队列 / 栈 → ArrayDeque;
- 优先级 → PriorityQueue;
- 并发阻塞 → ArrayBlockingQueue/LinkedBlockingQueue。
- 需键值对映射 →
Map:- 普通映射 → HashMap;
- 有序映射 → LinkedHashMap/TreeMap;
- 并发映射 → ConcurrentHashMap。
八、总结
Java 四大核心集合(List/Set/Queue/Map)是日常开发中最常用的数据结构,其设计遵循「接口统一、实现差异化」的原则:
- List 侧重「有序可重复」,提供索引访问;
- Set 侧重「无序不可重复」,提供去重能力;
- Queue 侧重「队列操作」,支持 FIFO / 优先级 / 阻塞;
- Map 侧重「键值对映射」,提供高效查找。

531

被折叠的 条评论
为什么被折叠?



