# 集合
集合是 Java 中用于存储、操作多个对象的容器,相比数组,集合的长度可变、支持丰富的操作方法、可以存储不同类型的对象(泛型约束后可统一类型)。

Java集合框架位于java.util包中
Collection是Set和List的父类,Collections是工具类,提供了对集合进行排序、遍历等多种算法的实现。
ArrayList: 有序(放进去顺序和拿出来顺序一致),可重复
HashSet: 无序(放进去顺序和拿出来顺序不一定一致),不可重复
接口可以继承接口
Java 集合框架主要分为两大分支:
- Collection 接口
:单列集合,存储一个个独立的元素
List:有序、可重复Set:无序、不可重复
- Map 接口:双列集合,存储键值对(Key-Value),Key 不可重复,Value 可重复
Collection 接口
Collection 是所有单列集合的父接口,定义了集合通用的方法。
Collection 通用方法
表格
| 方法 | 作用 |
|---|---|
boolean add(E e) |
向集合中添加一个元素 |
boolean remove(Object o) |
删除集合中指定的元素 |
boolean contains(Object o) |
判断集合是否包含指定元素 |
int size() |
返回集合中元素的个数 |
boolean isEmpty() |
判断集合是否为空 |
void clear() |
清空集合中所有元素 |
Object[] toArray() |
将集合转为数组 |
Iterator<E> iterator() |
获取集合的迭代器,用于遍历 |
Collection 是 Java 单列集合的根接口,核心分为三大子体系:List(有序可重复)、Set(无序不可重复)、Queue(队列 / 栈结构)。下面按子接口分类,从底层结构、性能、线程安全、适用场景等维度完整对比所有常用实现类。
List 接口实现类对比
List 核心特征:存取顺序一致、支持索引访问、允许元素重复,是开发中最常用的集合类型。
表格
| 实现类 | 底层数据结构 | 有序性 | 元素重复 | 线程安全 | 查询性能 | 首尾增删性能 | 中间增删性能 | 默认初始容量 | 扩容机制 | 核心特点 |
|---|---|---|---|---|---|---|---|---|---|---|
| ArrayList | Object 动态数组 | 插入有序,支持索引 | 允许 | 不安全 | 极快(O (1)) | 一般(可能触发扩容) | 慢(需移动元素 O (n)) | JDK8+ 默认为 0,首次 add 初始化为 10 | 每次扩容为原容量的 1.5 倍 | 最常用,查询为主的场景首选 |
| LinkedList | 双向链表(Node 节点) | 插入有序,支持索引 | 允许 | 不安全 | 慢(O (n)) | 极快(O (1)) | 慢(需遍历定位 O (n)) | 无初始容量,节点按需创建 | 无扩容机制,链表无限追加 | 同时实现了 Deque 接口,可做队列 / 栈 |
| Vector | Object 动态数组 | 插入有序,支持索引 | 允许 | 安全(方法全加 synchronized) |
较快(加锁有开销) | 慢(加锁 + 扩容) | 慢 | 默认初始容量 10 | 每次扩容为原容量的 2 倍 | 古老实现类,性能差,已基本淘汰 |
| Stack | 继承 Vector,数组实现 | 插入有序 | 允许 | 安全 | 一般 | 栈顶操作快 | 慢 | 同 Vector | 同 Vector | 栈结构(后进先出),官方已不推荐,优先用 ArrayDeque |
关键补充说明
- ArrayList 扩容细节 扩容时会创建新数组并将原数组元素拷贝过去,频繁扩容会影响性能;如果预知元素数量,建议通过
new ArrayList<>(initialCapacity)指定初始容量,减少扩容次数。 - LinkedList 的双重身份 它同时实现了
List和Deque接口,既可以当普通列表,也可以作为队列(Queue)或双端队列 / 栈使用。 - 为什么不推荐 Vector Vector 的线程安全是通过给所有方法加重量级锁实现的,并发性能极低;现代多线程场景更推荐用
CopyOnWriteArrayList替代。
Set 接口实现类对比
Set 核心特征:元素不可重复、大部分实现存取无序,主要用于去重、元素判重场景。
表格
| 实现类 | 底层数据结构 | 有序性 | 去重依据 | 线程安全 | 增删查性能 | 支持 null 元素 | 排序能力 | 适用场景 |
|---|---|---|---|---|---|---|---|---|
| HashSet | HashMap(哈希表:数组 + 链表 / 红黑树) | 无序(存取顺序不一致) | hashCode() + equals() |
不安全 | 快(O (1)) | 允许 1 个 null | 无 | 通用去重、判重,不要求顺序的场景 |
| LinkedHashSet | LinkedHashMap(哈希表 + 双向链表) | 插入有序(记录添加顺序) | hashCode() + equals() |
不安全 | 略慢于 HashSet(维护链表开销) | 允许 1 个 null | 无 | 需要去重且保证插入顺序的场景 |
| TreeSet | TreeMap(红黑树) | 排序有序(按元素大小排序) | compareTo() / compare() 返回 0 |
不安全 | 较快(O (logn)) | 不允许(null 无法比较) | 支持自然排序 / 定制排序 | 需要去重且对元素自动排序的场景 |
| EnumSet | 位向量(long 数组) | 按枚举定义顺序排列 | 枚举类型天然唯一 | 不安全 | 极快(位运算) | 不允许 | 按枚举常量排序 | 专门存储枚举类型元素,性能最高 |
关键补充说明
- HashSet 去重原理
- 调用元素的
hashCode()计算哈希值,确定在数组中的存储位置
- 若该位置无元素,直接存入
- 若该位置已有元素,调用
equals()
逐一比较内容
- 内容相同:判定为重复元素,添加失败
- 内容不同:以链表 / 红黑树形式挂载在该位置
自定义类要实现去重,必须同时重写
hashCode()和equals()方法。
- TreeSet 的排序前提
TreeSet 中的元素必须支持比较,两种实现方式:
- 元素类实现
Comparable接口(自然排序) - 创建 TreeSet 时传入
Comparator比较器(定制排序)
- LinkedHashSet 的有序本质 它在 HashSet 的哈希结构基础上,额外维护了一条双向链表记录元素的插入顺序,因此遍历顺序等于添加顺序。
Queue / Deque 接口实现类对比
Queue(队列)是先进先出(FIFO)的集合;Deque(双端队列)是 Queue 的子接口,两端都可插入删除,既能当队列也能当栈。
表格
| 实现类 | 底层数据结构 | 线程安全 | 有序性 | 核心特性 | 适用场景 |
|---|---|---|---|---|---|
| ArrayDeque | 循环数组 | 不安全 | 插入有序 | 双端操作 O (1),无容量上限,自动扩容 | 推荐作为栈 / 队列使用,性能远超 Stack 和 LinkedList |
| LinkedList | 双向链表 | 不安全 | 插入有序 | 双端操作 O (1),同时是 List 实现 | 需要同时兼顾列表和队列功能的场景 |
| PriorityQueue | 二叉堆(默认小顶堆) | 不安全 | 按优先级排序 | 元素按优先级出队,而非插入顺序 | 优先级任务、TOP K 等需要按权重排序的场景 |
关键补充说明
- ArrayDeque 是栈和队列的首选:作为栈比古老的
Stack快,作为队列比LinkedList快(数组的缓存命中率更高)。 - PriorityQueue 注意点:元素必须支持比较(同 TreeSet),默认是自然升序(小顶堆),可通过 Comparator 改为大顶堆;不支持存储 null。
并发安全的 Collection 实现(java.util.concurrent)
上述所有实现类(除 Vector/Stack)都不是线程安全的,多线程场景推荐使用并发包下的专用实现:
表格
| 接口 | 并发实现类 | 线程安全机制 | 特点 |
|---|---|---|---|
| List | CopyOnWriteArrayList |
写时复制(Copy-On-Write) | 读无锁,写时复制新数组,读多写少场景性能好 |
| Set | CopyOnWriteArraySet |
基于 CopyOnWriteArrayList 实现 | 写时复制,适合读多写少的并发去重场景 |
| Set | ConcurrentSkipListSet |
跳表结构 + CAS | 并发有序集合,替代 TreeSet 的并发版本 |
| Queue | ArrayBlockingQueue |
数组 + 重入锁 | 有界阻塞队列,常用于生产者消费者模型 |
| Queue | LinkedBlockingQueue |
链表 + 双锁 | 可设置容量的阻塞队列,吞吐量高于 ArrayBlockingQueue |
List 接口(有序、可重复)
List 集合的元素存取有序、支持索引、允许重复,常用实现类有 ArrayList、LinkedList、Vector。
List 特有方法(带索引操作)
表格
| 方法 | 作用 |
|---|---|
void add(int index, E element) |
在指定索引位置插入元素 |
E get(int index) |
获取指定索引位置的元素 |
E remove(int index) |
删除指定索引位置的元素 |
E set(int index, E element) |
修改指定索引位置的元素 |
int indexOf(Object o) |
查找元素第一次出现的索引 |
@Test
public void test1() {
//数组最大问题是长度固定,而且要操作下标
Student[] array = new Student[3];
ArrayList<Student> list = new ArrayList<>();
Student student1 = new Student();
Student student2 = new Student();
Student student3 = new Student();
Student student4 = new Student();
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student1);
//有序可重复
//有序:你放进去的顺序和拿出来的顺序一致
//ArrayList<String> list1 = new ArrayList<>();
List<String> list1 = new ArrayList<>();
list1.add("Java");
list1.add("UI");
list1.add("H5");
list1.add("H5");
list1.add("aa");
for (String str : list1) {
System.out.println(str);
}
System.out.println("-------------------");
//无序不重复
//无序:放进去顺序和拿出来的顺序可能是不一致的
//HashSet<String> set = new HashSet<String>();
Set<String> set = new HashSet<>();
set.add("Java");
set.add("UI");
set.add("H5");
set.add("H5");
set.add("aa");
for (String str : set) {
System.out.println(str);
}
}
1.8版本以及之后的版本泛型后面的数据类型可以不用写
ArrayList和LinkedList区别
ArrayList和LinkedList的大致区别如下:
1. ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2. 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3. 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
| 实现类 | 底层结构 | 特点 | 线程安全 | 适用场景 |
|---|---|---|---|---|
ArrayList |
动态数组 | 查询快、增删慢 | 不安全 | 频繁查询、少量增删 |
LinkedList |
双向链表 | 查询慢、首尾增删快 | 不安全 | 频繁首尾增删、队列 / 栈场景 |
Vector |
动态数组 | 查询快、增删慢 | 安全(方法加锁) | 多线程场景(已不推荐) |
Map
Map 用于存储键值对(Key-Value),Key 唯一不可重复,Value 可重复。
Map 常用方法
| 方法 | 作用 |
|---|---|
V put(K key, V value) |
添加键值对;key 已存在则覆盖 value |
V get(Object key) |
根据 key 获取对应的 value |
V remove(Object key) |
根据 key 删除键值对 |
boolean containsKey(Object key) |
判断是否包含指定的 key |
boolean containsValue(Object value) |
判断是否包含指定的 value |
int size() |
返回键值对的数量 |
Set<K> keySet() |
获取所有 key 组成的 Set 集合 |
Collection<V> values() |
获取所有 value 组成的集合 |
Set<Map.Entry<K,V>> entrySet() |
获取所有键值对对象的集合 |
Map 实现类对比
| 实现类 | 底层结构 | 线程安全 | 特点 |
|---|---|---|---|
HashMap |
哈希表(数组 + 链表 / 红黑树) | 不安全 | 效率高,允许 key/value 为 null |
LinkedHashMap |
哈希表 + 双向链表 | 不安全 | 可保证插入顺序 |
TreeMap |
红黑树 | 不安全 | 可对 key 自动排序 |
Hashtable |
哈希表 | 安全 | 效率低,不允许 key/value 为 null(已不推荐) |

Map遍历方式
@Test
public void test55() {
Map<String, String> map = new HashMap<>();
map.put("cn", "中国");
map.put("us", "美国");
map.put("uk", "英国");
String country = map.get("cn");
System.out.println(country);
// 方式1:遍历 entrySet(推荐,效率高)
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
// 方式2:遍历 iterator
System.out.println("----------------");
Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
// 方式3:遍历 key,通过 key 找 value
System.out.println("-----------------");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key + " : " + map.get(key));
}
System.out.println("-----------------");
// 方式4:Lambda 表达式(Java 8+)
map.forEach((k, v) -> System.out.println(k + " → " + v));
}
###
集合遍历:迭代器 Iterator
Iterator 是 Java 集合的专用遍历工具,所有 Collection 集合都支持迭代器遍历。
常用方法
boolean hasNext():判断是否还有下一个元素E next():获取下一个元素void remove():删除当前元素
示例
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) {
it.remove(); // 迭代过程中删除元素,必须用迭代器的 remove
}
}
⚠️ 注意:迭代过程中不能用集合自身的
add/remove方法修改集合,否则会抛出并发修改异常ConcurrentModificationException。
Collections 工具类
java.util.Collections 是集合的工具类,提供了大量静态方法操作集合。
表格
| 方法 | 作用 |
|---|---|
static void sort(List list) |
对 List 集合自然排序 |
static void reverse(List list) |
反转 List 集合元素 |
static void shuffle(List list) |
随机打乱 List 集合元素 |
static int max(Collection coll) |
获取集合中的最大值 |
static int min(Collection coll) |
获取集合中的最小值 |
static boolean addAll(Collection c, T... elements) |
批量添加多个元素 |
泛型基础
泛型(Generic)可以在编译期约束集合存储的数据类型,避免类型转换异常,让代码更安全。
基本使用
// 约束集合只能存 String 类型
List<String> list = new ArrayList<String>();
// Java 7+ 可简写为
List<String> list = new ArrayList<>();
泛型通配符
<?>:任意类型通配符<? extends E>:上限通配符,只能是 E 或 E 的子类<? super E>:下限通配符,只能是 E 或 E 的父类
集合选型建议
需要键值对 → 选 Map 体系
- 排序需求用
TreeMap - 保证插入顺序用
LinkedHashMap - 普通场景用
HashMap
单列元素 → 选 Collection 体系
- 可重复、需要索引用 List
- 频繁查询用
ArrayList - 频繁首尾增删用
LinkedList - 不可重复用 Set
- 去重即可用
HashSet - 保证插入顺序用
LinkedHashSet - 需要排序用
TreeSet
选大体系
- 需要索引、允许重复 → 选 List
- 需要去重、不允许重复 → 选 Set
- 需要队列 / 栈、先进先出 / 后进先出 → 选 Queue/Deque
选具体实现
- List 场景:
- 频繁查询、少量增删 → ArrayList
- 频繁首尾增删、做队列 / 栈 → LinkedList / ArrayDeque
- 多线程读多写少 → CopyOnWriteArrayList
- Set 场景:
- 普通去重、无需顺序 → HashSet
- 去重 + 保留插入顺序 → LinkedHashSet
- 去重 + 自动排序 → TreeSet
- 队列 / 栈场景:
- 普通栈 / 队列 → ArrayDeque
- 优先级排序 → PriorityQueue
- 并发阻塞场景 → ArrayBlockingQueue