Java 集合框架(Java Collections Framework, JCF)是 Java 开发中最核心、最常用的工具之一,用于高效地存储和操作一组对象。合理选择集合类型对程序的性能、内存占用、线程安全性和代码可读性至关重要。
下面从 分类结构、核心特性、适用场景 三个维度系统介绍 Java 集合,并给出"什么时候用什么集合"的实用建议。
一、Java 集合框架整体结构
Collection
├── List(有序、可重复)
│ ├── ArrayList
│ ├── LinkedList
│ └── Vector / Stack(已过时,不推荐)
│
├── Set(无序、不可重复)
│ ├── HashSet
│ ├── LinkedHashSet
│ └── TreeSet
│
└── Queue(队列,FIFO 或优先级)
├── LinkedList(也实现 Deque)
├── ArrayDeque
├── PriorityQueue
└── BlockingQueue(如 LinkedBlockingQueue)
Map(键值对,非 Collection 子接口)
├── HashMap
├── LinkedHashMap
├── TreeMap
├── Hashtable(过时)
└── ConcurrentHashMap
⚠️ 注意:
Map不属于Collection接口体系,但通常被归为集合框架的一部分。
二、各类集合详解与使用场景
1. List ------ 有序、允许重复、按索引访问
| 实现类 | 底层结构 | 查询 | 插入/删除 | 线程安全 | 适用场景 |
|---|---|---|---|---|---|
| ArrayList | 动态数组 | O(1)(按索引) | O(n)(中间插入/删除) | ❌ 否 | ✅ 绝大多数场景首选• 频繁随机访问• 数据量大且读多写少 |
| LinkedList | 双向链表 | O(n) | O(1)(首尾操作) | ❌ 否 | • 频繁在首尾增删(如栈、队列)• 很少随机访问 |
📌 建议 :除非明确需要频繁头尾操作,否则优先用
ArrayList。LinkedList内存开销大(每个节点存前后指针),缓存局部性差。
2. Set ------ 无重复元素
| 实现类 | 底层 | 是否有序 | 是否排序 | 时间复杂度(add/contains) | 适用场景 |
|---|---|---|---|---|---|
| HashSet | HashMap | ❌ 无序 | ❌ | O(1) 平均 | ✅ 去重 + 快速查找• 不关心顺序• 如用户ID集合、黑名单 |
| LinkedHashSet | LinkedHashMap | ✅ 插入顺序 | ❌ | O(1) | • 需要保持插入顺序的去重集合• 如记录访问历史 |
| TreeSet | TreeMap(红黑树) | ✅ 升序(自然 or Comparator) | ✅ | O(log n) | • 需要自动排序 • 如排行榜、范围查询(subSet()) |
📌 关键点:
HashSet依赖hashCode()和equals(),务必正确重写!TreeSet要求元素实现Comparable或提供Comparator。
3. Map ------ 键值对存储
| 实现类 | 底层 | 是否有序 | 是否排序 | 线程安全 | 适用场景 |
|---|---|---|---|---|---|
| HashMap | 数组+链表/红黑树(JDK8+) | ❌ | ❌ | ❌ | ✅ 最常用 Map• 高频 get/put• 如缓存、配置项 |
| LinkedHashMap | HashMap + 双向链表 | ✅ 插入/访问顺序 | ❌ | ❌ | • LRU 缓存(重写 removeEldestEntry)• 保持插入或访问顺序 |
| TreeMap | 红黑树 | ✅ 升序(按 key) | ✅ | ❌ | • 按 key 范围查询(subMap)• 如按时间戳排序的日志 |
| ConcurrentHashMap | 分段锁 / CAS(JDK8+) | ❌ | ❌ | ✅ | ✅ 高并发场景首选 • 多线程共享 Map• 替代 Hashtable 或 synchronizedMap |
📌 注意:
Hashtable和Collections.synchronizedMap()性能差(全表锁),避免使用。ConcurrentHashMap不支持nullkey/value。
4. Queue / Deque ------ 队列与双端队列
| 实现类 | 特性 | 适用场景 |
|---|---|---|
| ArrayDeque | 基于循环数组,无界 | ✅ 栈、队列首选 • 替代 Stack 和 LinkedList 做队列 |
| LinkedList | 双向链表 | • 需要同时当 List 和 Queue 用(不推荐混用) |
| PriorityQueue | 二叉堆 | • 优先级队列(如任务调度、Top K 问题) |
BlockingQueue (如 LinkedBlockingQueue) |
阻塞队列 | • 生产者-消费者模型• 线程池工作队列 |
📌 建议:
- 用
ArrayDeque代替Stack(Stack继承Vector,性能差且线程安全冗余)。- 高并发生产消费 → 用
BlockingQueue。
三、线程安全集合选择
| 场景 | 推荐方案 |
|---|---|
| 单线程 | 直接用 ArrayList, HashMap, HashSet |
| 多线程只读 | 普通集合 + Collections.unmodifiableXxx() 包装 |
| 多线程读写(高性能) | ConcurrentHashMap, CopyOnWriteArrayList, ConcurrentLinkedQueue |
| 多线程读写(强一致性) | Collections.synchronizedXxx()(性能差,慎用) |
🔥 重点:
CopyOnWriteArrayList:写时复制 ,适合读多写少(如监听器列表)。ConcurrentHashMap:支持高并发读写,不要用synchronizedMap。
四、快速决策指南(什么时候用什么?)
| 你的需求 | 推荐集合 |
|---|---|
| 存一组数据,经常通过下标访问 | ArrayList |
| 需要去重,不关心顺序 | HashSet |
| 需要去重,且保持插入顺序 | LinkedHashSet |
| 需要自动排序(如字典序) | TreeSet 或 TreeMap |
| 存键值对,高频查询 | HashMap |
| 存键值对,需要按插入顺序遍历 | LinkedHashMap |
| 多线程共享 Map | ConcurrentHashMap |
| 实现栈(LIFO) | ArrayDeque(调用 push/pop) |
| 实现队列(FIFO) | ArrayDeque(调用 offer/poll) |
| 任务按优先级处理 | PriorityQueue |
| 生产者-消费者模型 | BlockingQueue(如 LinkedBlockingQueue) |
五、最佳实践总结
-
默认选择:
- List →
ArrayList - Set →
HashSet - Map →
HashMap
- List →
-
需要顺序?
- 插入顺序 →
LinkedHashSet/LinkedHashMap - 排序 →
TreeSet/TreeMap
- 插入顺序 →
-
高并发?
- 用
java.util.concurrent包下的并发集合,不要自己加 synchronized。
- 用
-
避免过时类:
- 不要用
Vector,Stack,Hashtable。
- 不要用
-
注意 null 值:
ConcurrentHashMap、TreeMap等不支持nullkey。
-
初始化容量:
- 已知数据量大时,构造时指定初始容量(如
new ArrayList(1000)),避免频繁扩容。
- 已知数据量大时,构造时指定初始容量(如
结语
Java 集合不是"随便选一个能用就行",而是根据访问模式、数据规模、并发需求、顺序要求 综合权衡的结果。掌握每种集合的内部结构 和时间复杂度特性,才能写出高性能、可维护的代码。
💡 记住一句话 :
"读多写少用 CopyOnWrite,高并发用 Concurrent,排序用 Tree,去重用 Hash,顺序用 Linked,队列栈用 ArrayDeque。"