Java 集合框架体系总览 | JavaSE
Java 集合框架体系总览
一、学习目标
完成本章后,你应该能够:
- 能够解释什么是 Java 集合框架(Java Collections Framework)。
- 能够说明集合与数组在长度、数据组织方式和适用场景上的区别。
- 能够画出 Java 集合框架的核心体系结构,理解
Collection与Map两条主线。 - 能够区分
List、Set、Queue、Map在数据组织方式上的核心差异。 - 能够理解“接口 → 实现类”的设计思想,并根据需求初步选择集合类型。
- 能够说明泛型在集合中的作用,以及为什么集合泛型不能直接使用基本数据类型。
- 能够了解 JDK 21 引入的
SequencedCollection、SequencedSet、SequencedMap在集合体系中的位置。
二、核心知识
2.1 为什么需要集合
在学习集合之前,我们已经使用过数组保存一组数据:
String[] names = new String[3];
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
数组当然可以保存多个数据,但它存在一个非常明显的特点:
数组创建完成后,长度固定。
例如:
String[] names = new String[3];
这个数组只能拥有 3 个位置。
假设现在开发一个学生管理系统:
今天有 30 个学生
明天增加 12 个学生
后天删除 5 个学生
下周又增加 100 个学生
如果使用固定长度数组管理这种不断变化的数据,就会非常麻烦。
因此 Java 提供了一套更适合管理动态数据的容器体系:
Java 集合框架(Java Collections Framework)。
2.2 什么是 Java 集合框架
Java 集合框架是一套用于:
- 保存数据;
- 组织数据;
- 查询数据;
- 修改数据;
- 遍历数据;
- 排序数据;
- 去重数据;
的统一 API 体系。
它不是某一个类,而是一整个框架。
可以把集合框架理解为一个“数据容器工具箱”。
不同的数据特点,对应不同的容器。
例如:
班级学生名单
→ List
用户角色集合
→ Set
待处理任务
→ Queue
用户名 → 用户对象
→ Map
因此学习集合真正重要的不是:
“把 ArrayList、HashSet、HashMap 的方法背下来。”
而是建立下面的思维:
数据有什么特点?
↓
应该使用哪一种数据结构?
↓
选择哪个集合接口?
↓
选择哪个具体实现类?
2.3 Java 集合框架的三个核心组成
Java Collections Framework 可以从三个部分理解。
第一部分:接口
接口定义集合应该具备什么能力。
例如:
Collection
List
Set
Queue
Map
接口描述的是:
“这种集合应该能做什么。”
第二部分:实现类
实现类负责真正完成数据存储。
例如:
ArrayList
LinkedList
HashSet
LinkedHashSet
TreeSet
HashMap
LinkedHashMap
TreeMap
例如:
List<String> names = new ArrayList<>();
这里:
List
是接口。
ArrayList
是实现类。
第三部分:算法与工具
Java 还提供了大量对集合进行处理的算法和工具,例如:
排序
查找
反转
随机打乱
最大值
最小值
后续我们会学习:
Collections
工具类。
因此可以形成:
Java Collections Framework
│
├── 接口
│
├── 实现类
│
└── 操作集合的算法与工具
2.4 集合与数组的区别
数组和集合都可以保存多个数据,但两者定位不同。
| 对比项 | 数组 | 集合 | | ------------ | ------------------ | ---------------------------- | | 长度 | 创建后固定 | 通常可以动态变化 | | 基本数据类型 | 可以直接保存 | 泛型不能直接使用基本数据类型 | | 引用数据类型 | 可以 | 可以 | | API 丰富度 | 较少 | 非常丰富 | | 数据结构 | 主要是连续索引结构 | 数组、链表、哈希表、树等 | | 增删数据 | 相对麻烦 | 通常更加方便 | | 典型用途 | 长度明确的数据 | 数量动态变化的业务数据 |
例如:
int[] numbers = {10, 20, 30};
数组可以直接保存:
int
但是泛型集合不能写:
// 错误
List<int> numbers;
需要使用包装类:
List<Integer> numbers = new ArrayList<>();
实际使用:
numbers.add(10);
numbers.add(20);
这里发生了自动装箱:
int
↓
Integer
这正是前面学习包装类与泛型的重要应用。
2.5 Java 集合框架的两条核心主线
初学阶段可以首先把整个集合体系分成两条主线:
Java Collections Framework
│
├── Collection
│
│ └── 管理一个一个的元素
│
└── Map
└── 管理 key-value 键值对
2.6 Collection:单个元素组成的集合
Collection<E> 表示:
一组元素组成的集合。
例如:
"Java"
"MySQL"
"Redis"
可以放入一个 Collection 体系的集合中。
Collection 是 Java 集合层次结构中的核心根接口之一,其上层还有:
Iterable
因此更准确地表示:
Iterable<E>
│
Collection<E>
Collection 下面继续发展出了不同类型的数据结构。
初学阶段重点关注:
Collection
│
├── List
├── Set
└── Queue
2.7 List:有顺序、可以重复的数据
List 可以简单理解为:
按位置排列的一组数据。
例如:
第 0 个:Java
第 1 个:MySQL
第 2 个:Java
特点:
有确定的元素顺序
允许重复
可以通过位置访问元素
典型实现:
ArrayList
LinkedList
例如:
List<String> technologies = new ArrayList<>();
technologies.add("Java");
technologies.add("MySQL");
technologies.add("Java");
三个元素都可以保存。
2.8 Set:强调元素唯一性
Set 最重要的语义是:
不允许出现重复元素。
例如用户拥有的角色:
ADMIN
USER
USER
如果业务要求:
USER 只能存在一次
就很适合使用 Set。
典型实现:
HashSet
LinkedHashSet
TreeSet
需要特别注意:
不能简单地把所有 Set 都理解成“无序集合”。
更加准确的说法是:
Set接口本身主要规定“元素不重复”,并不统一保证某一种具体遍历顺序。
具体实现不同:
HashSet
→ 不保证特定的遍历顺序
LinkedHashSet
→ 具有定义明确的 encounter order
TreeSet
→ 按排序规则维护元素顺序
这些内容会在后面的 Set 专章中详细学习。
2.9 Queue:面向“等待处理”的数据
Queue 表示队列(Queue)。
它非常适合:
保存暂时等待处理的数据。
生活中最典型的例子就是排队。
A 先来
B 后来
C 最后来
通常:
A → B → C
按照某种规则依次处理。
例如:
消息队列
任务队列
打印任务
线程池任务
请求缓冲
都具有类似思想。
Queue 也是:
Collection
体系的一部分。
本套课程不单独为 Queue 设置章节,但在后续多线程、并发和其他工程技术中会再次遇到队列思想。
2.10 Map:键值对数据
Map 与 Collection 最大的区别在于:
Collection 更关注:
一个一个的数据
例如:
Java
MySQL
Redis
Map 更关注:
key → value
例如:
10001 → 张三
10002 → 李四
10003 → 王五
Java 代码:
Map<Integer, String> students = new HashMap<>();
students.put(10001, "张三");
students.put(10002, "李四");
Map 的核心规则之一是:
一个 key 最多对应一个 value。
例如:
students.put(10001, "张三");
students.put(10001, "李四");
第二次使用相同 key 存放数据时,会影响这个 key 对应的映射关系。
Map 不是:
Collection
的子接口。
但是:
Map属于整个 Java Collections Framework。
因此:
Collection != Java全部集合框架
Java集合框架
= Collection体系 + Map体系 + 相关实现、算法与工具
这一点非常重要。
2.11 一个最简单的集合选择模型
现在先建立第一版集合选择思维。
情况一:只保存一批元素
先考虑:
Collection
情况二:允许重复,并且关心元素位置
考虑:
List
常见:
ArrayList
LinkedList
情况三:要求元素不能重复
考虑:
Set
常见:
HashSet
LinkedHashSet
TreeSet
情况四:数据需要排队处理
考虑:
Queue / Deque
情况五:需要建立键和值之间的映射
考虑:
Map
常见:
HashMap
LinkedHashMap
TreeMap
最终可以形成:
我要存什么数据?
│
├── 单个元素
│ ↓
│ Collection
│ │
│ ├── 可以重复、强调位置 → List
│ ├── 不能重复 → Set
│ └── 等待处理 → Queue
│
└── key-value
↓
Map
2.12 接口与实现类为什么要分开
观察下面的代码:
List<String> names = new ArrayList<>();
为什么不直接写:
ArrayList<String> names = new ArrayList<>();
两种写法都可以。
但第一种代码体现了一种非常重要的设计思想:
面向接口编程(Programming to an Interface)。
左边:
List<String>
表示我们真正依赖的是:
List 能力
右边:
new ArrayList<>()
决定具体使用哪一种实现。
未来如果需求变化:
List<String> names = new LinkedList<>();
上层大量使用 List API 的代码往往不需要跟着改变。
这就是:
接口
↓
描述能力
实现类
↓
负责具体实现
也是 Java 框架技术中极其重要的设计思想。
2.13 泛型为什么与集合关系这么密切
集合可以保存各种对象:
String
Student
User
Product
Order
如果集合完全不知道里面保存的是什么类型,那么每次取数据都会非常危险。
泛型解决的就是:
集合中的元素类型约束。
例如:
List<String> names = new ArrayList<>();
意味着:
这个 List 主要用于保存 String
因此:
names.add("Java");
合法。
而:
// 编译错误
names.add(100);
不合法。
如果是:
List<Student> students = new ArrayList<>();
那么集合就专门管理:
Student
对象。
这就是前面学习泛型后立刻进入集合框架的原因。
三、使用方法
3.1 List、Set、Map 的第一段对比代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionsOverviewDemo {
public static void main(String[] args) {
// List:允许重复,并维护元素顺序
List<String> learningOrder = new ArrayList<>();
learningOrder.add("泛型");
learningOrder.add("集合");
learningOrder.add("集合");
// Set:强调元素唯一性
Set<String> tags = new HashSet<>();
tags.add("Java");
tags.add("集合");
tags.add("Java");
// Map:key-value
Map<String, Integer> scores = new HashMap<>();
scores.put("小林", 90);
scores.put("小林", 95);
System.out.println(learningOrder);
System.out.println(tags.size());
System.out.println(scores.get("小林"));
}
}
这里暂时不要研究底层原理。
只需要观察三个模型:
List
Java
集合
集合
允许重复。
Set
Java
集合
重复的 "Java" 不会形成两个相同元素。
Map
小林 → 95
通过 key 找 value。
3.2 集合通常使用接口作为变量类型
推荐逐渐养成:
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<String, Integer> map = new HashMap<>();
这种写法。
它体现:
变量依赖抽象接口
具体对象选择实现类
但是也不要机械化。
如果业务确实需要某个实现类独有的能力,就可以直接使用对应实现类型。
3.3 创建集合时使用泛型
不要写:
List list = new ArrayList();
这种原始类型(Raw Type)代码作为现代 Java 的主方案。
应该明确元素类型:
List<String> list = new ArrayList<>();
或者:
List<Student> students = new ArrayList<>();
这样编译器可以帮助我们进行类型检查。
四、原理与进阶
4.1 集合框架本质上建立在数据结构之上
从更底层看:
ArrayList
→ 动态数组思想
LinkedList
→ 双向链表
HashSet
→ 哈希结构
HashMap
→ 哈希表
TreeSet
→ 有序树结构
TreeMap
→ 有序映射结构
因此:
Java 集合框架并不是“很多 API 的堆积”,而是数据结构在 Java 工程中的标准化实现。
后面学习每个具体集合时,都需要同时回答两个问题:
1. API 怎么使用?
2. 底层数据结构为什么决定了它的性能特点?
4.2 JDK 21:Sequenced Collections
从 JDK 21 开始,Java 集合框架增加了三组重要接口:
SequencedCollection
SequencedSet
SequencedMap
其中:
SequencedCollection<E>
表示:
具有明确 encounter order,并且能够统一访问两端以及反向视图的集合。
典型能力包括:
getFirst();
getLast();
addFirst();
addLast();
removeFirst();
removeLast();
reversed();
JDK 21 中,例如:
List
Deque
都属于 SequencedCollection 体系。
而:
LinkedHashSet
TreeSet
等具有明确顺序语义的 Set 类型,也进入了 SequencedSet 体系。
因此现代 Java 中更精确的部分接口关系可以理解为:
Iterable
│
Collection
│
├── Queue
│ │
│ └── Deque ─────────┐
│ │
├── SequencedCollection ──┤
│ │ │
│ ├── List │
│ └── SequencedSet
│ │
└── Set ─────────┘
现阶段不需要背整个继承图。
只需要知道:
JDK 21 对“具有明确元素顺序的集合”提供了更加统一的抽象。
后续学习具体集合时再逐步使用这些能力。
4.3 “有序”和“排序”不是一回事
这是集合学习中非常重要的一组术语。
有序
通常强调:
元素具有定义明确的 encounter order。
例如:
A
B
C
先后关系明确。
排序
强调:
元素按照某种比较规则排列。
例如:
1
3
7
10
或者:
Alice
Bob
Jack
因此:
保持插入顺序
和:
按照大小排序
完全不是一回事。
后面的:
LinkedHashSet
TreeSet
LinkedHashMap
TreeMap
会不断遇到这个区别。
五、实践应用
5.1 博客文章列表
例如博客首页需要展示:
第一篇
第二篇
第三篇
并允许多篇文章存在。
可以考虑:
List<Article>
5.2 用户权限
某个用户拥有:
ADMIN
EDITOR
USER
同一个角色通常不应该重复。
可以考虑:
Set<Role>
5.3 用户 ID 查询用户
业务经常需要:
userId → User
可以考虑:
Map<Long, User>
5.4 任务处理
例如:
任务 A
任务 B
任务 C
需要按照一定顺序等待处理。
可以考虑:
Queue<Task>
甚至:
Deque<Task>
六、常见问题
6.1 Collection 和 Collections 是同一个东西吗?
不是。
Collection
是集合体系中的核心接口。
而:
Collections
是 Java 提供的集合工具类。
两者名字只差一个:
s
但含义完全不同。
Collections 会在本组最后的综合章节中专门学习。
6.2 Map 是 Collection 的子接口吗?
不是。
错误理解:
Collection
└── Map
正确理解:
Java Collections Framework
├── Collection 体系
└── Map 体系
6.3 集合都能自动扩容吗?
不能这样绝对描述。
很多常见通用实现,例如:
ArrayList
HashMap
都会根据数据变化调整内部存储结构。
但:
Collection接口本身并没有承诺“所有实现都是无限动态扩容”。
具体能力取决于实现类。
6.4 集合能保存 int 吗?
泛型不能写:
List<int>
应该写:
List<Integer>
使用时:
list.add(10);
Java 会利用自动装箱完成:
int → Integer
6.5 Set 就一定是无序的吗?
不能这样绝对描述。
Set 的核心语义是:
元素不重复
不同实现具有不同顺序特性:
HashSet
→ 不保证特定 encounter order
LinkedHashSet
→ 有明确 encounter order
TreeSet
→ 根据排序规则维护顺序
6.6 ArrayList 和 LinkedList 到底怎么选?
目前先不要急着下结论。
它们都属于:
List
但是底层数据结构不同。
后续会分别学习:
05-06 ArrayList 使用与底层原理
05-07 LinkedList 与双向链表
到那时再从:
查询
插入
删除
内存结构
业务场景
系统比较。
七、练习与验收
7.1 知识问答
- 什么是 Java Collections Framework?
- 为什么已经有数组,Java 还需要集合框架?
- 数组与集合最明显的使用差异有哪些?
- Java 集合框架为什么不能简单理解为
Collection? Collection与Map在数据组织方式上有什么区别?List最核心的数据特征是什么?Set最核心的数据特征是什么?Queue主要适合解决什么类型的问题?Map中 key 和 value 是什么关系?- 为什么
Map不属于Collection子接口,但仍属于 Java Collections Framework? - 接口与集合实现类分别承担什么职责?
- 为什么集合通常与泛型一起使用?
- 为什么不能声明
List<int>? Collection与Collections有什么区别?- “有序”和“排序”有什么区别?
- JDK 21 中
SequencedCollection主要解决什么问题?
7.2 代码阅读
阅读代码:
List<String> list = new ArrayList<>();
list.add("Java");
list.add("MySQL");
list.add("Java");
Set<String> set = new HashSet<>();
set.add("Java");
set.add("MySQL");
set.add("Java");
回答:
list中保存了几个元素?set中从“元素唯一性”角度会保存几个不同元素?- 为什么两种集合表现不同?
- 两个变量分别使用什么接口作为声明类型?
- 两个对象分别由什么实现类创建?
阅读代码:
Map<Integer, String> users = new HashMap<>();
users.put(1, "张三");
users.put(2, "李四");
users.put(1, "王五");
回答:
- Map 中的数据按照什么形式保存?
1在这里是什么?"王五"在这里是什么?- 同一个 key 再次执行
put后应该重点观察什么? - Map 是否属于 Collection 子接口?
7.3 手写代码
- 创建一个
List<String>,保存 5 门你正在学习的技术,其中至少出现一个重复元素。 - 创建一个
Set<String>,保存多个技术标签,并故意重复添加一个标签。 - 创建一个
Map<String, Integer>,保存“课程名称 → 学习进度”。 - 创建一个
List<Student>,向其中保存三个学生对象。 - 尝试编写
List<int>,观察编译器结果,再修改为正确形式并说明原因。
7.4 Debug
下面代码存在问题:
List<int> scores = new ArrayList<>();
scores.add(90);
scores.add(100);
要求:
- 找出错误位置。
- 解释为什么泛型不能使用
int。 - 修复代码。
- 说明修复后
90加入集合时涉及什么语言机制。
下面代码存在设计认知问题:
Collection<String> data = new HashMap<>();
要求:
- 判断能否编译。
- 说明
Collection与Map的关系。 - 分别写出一个正确的 Collection 体系变量和 Map 体系变量。
7.5 综合训练
设计一个简单的“星雨笔录数据容器模型”。
系统需要保存:
1. 首页最新文章列表
2. 一篇文章的标签
3. 用户 ID 与用户对象的对应关系
4. 后台待处理任务
要求:
- 分别选择
List、Set、Map、Queue中最合适的数据模型。 - 写出对应泛型声明。
- 说明每一次选择依据。
- 暂时不考虑数据库,只讨论 Java 内存中的数据组织方式。
7.6 本章验收
在不查看资料的情况下,完成下面任务:
- 能够画出
Collection / List / Set / Queue / Map的基本关系。 - 能够解释为什么
Map不继承Collection。 - 能够分别举出 List、Set、Map 的一个真实业务例子。
- 能够解释数组与集合至少 4 个区别。
- 能够解释集合为什么需要泛型。
- 能够解释接口与实现类之间的关系。
- 能够说明 JDK 21
SequencedCollection的基本定位。 - 面对一个业务数据需求,能够先分析数据特征,再选择集合,而不是直接默认使用
ArrayList。