Java 集合框架体系总览 | JavaSE

Java 集合框架体系总览

一、学习目标

完成本章后,你应该能够:

  • 能够解释什么是 Java 集合框架(Java Collections Framework)。
  • 能够说明集合与数组在长度、数据组织方式和适用场景上的区别。
  • 能够画出 Java 集合框架的核心体系结构,理解 CollectionMap 两条主线。
  • 能够区分 ListSetQueueMap 在数据组织方式上的核心差异。
  • 能够理解“接口 → 实现类”的设计思想,并根据需求初步选择集合类型。
  • 能够说明泛型在集合中的作用,以及为什么集合泛型不能直接使用基本数据类型。
  • 能够了解 JDK 21 引入的 SequencedCollectionSequencedSetSequencedMap 在集合体系中的位置。

二、核心知识

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 知识问答

  1. 什么是 Java Collections Framework?
  2. 为什么已经有数组,Java 还需要集合框架?
  3. 数组与集合最明显的使用差异有哪些?
  4. Java 集合框架为什么不能简单理解为 Collection
  5. CollectionMap 在数据组织方式上有什么区别?
  6. List 最核心的数据特征是什么?
  7. Set 最核心的数据特征是什么?
  8. Queue 主要适合解决什么类型的问题?
  9. Map 中 key 和 value 是什么关系?
  10. 为什么 Map 不属于 Collection 子接口,但仍属于 Java Collections Framework?
  11. 接口与集合实现类分别承担什么职责?
  12. 为什么集合通常与泛型一起使用?
  13. 为什么不能声明 List<int>
  14. CollectionCollections 有什么区别?
  15. “有序”和“排序”有什么区别?
  16. 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");

回答:

  1. list 中保存了几个元素?
  2. set 中从“元素唯一性”角度会保存几个不同元素?
  3. 为什么两种集合表现不同?
  4. 两个变量分别使用什么接口作为声明类型?
  5. 两个对象分别由什么实现类创建?

阅读代码:

Map<Integer, String> users = new HashMap<>();

users.put(1, "张三");
users.put(2, "李四");
users.put(1, "王五");

回答:

  1. Map 中的数据按照什么形式保存?
  2. 1 在这里是什么?
  3. "王五" 在这里是什么?
  4. 同一个 key 再次执行 put 后应该重点观察什么?
  5. Map 是否属于 Collection 子接口?

7.3 手写代码

  1. 创建一个 List<String>,保存 5 门你正在学习的技术,其中至少出现一个重复元素。
  2. 创建一个 Set<String>,保存多个技术标签,并故意重复添加一个标签。
  3. 创建一个 Map<String, Integer>,保存“课程名称 → 学习进度”。
  4. 创建一个 List<Student>,向其中保存三个学生对象。
  5. 尝试编写 List<int>,观察编译器结果,再修改为正确形式并说明原因。

7.4 Debug

下面代码存在问题:

List<int> scores = new ArrayList<>();

scores.add(90);
scores.add(100);

要求:

  1. 找出错误位置。
  2. 解释为什么泛型不能使用 int
  3. 修复代码。
  4. 说明修复后 90 加入集合时涉及什么语言机制。

下面代码存在设计认知问题:

Collection<String> data = new HashMap<>();

要求:

  1. 判断能否编译。
  2. 说明 CollectionMap 的关系。
  3. 分别写出一个正确的 Collection 体系变量和 Map 体系变量。

7.5 综合训练

设计一个简单的“星雨笔录数据容器模型”。

系统需要保存:

1. 首页最新文章列表
2. 一篇文章的标签
3. 用户 ID 与用户对象的对应关系
4. 后台待处理任务

要求:

  1. 分别选择 ListSetMapQueue 中最合适的数据模型。
  2. 写出对应泛型声明。
  3. 说明每一次选择依据。
  4. 暂时不考虑数据库,只讨论 Java 内存中的数据组织方式。

7.6 本章验收

在不查看资料的情况下,完成下面任务:

  • 能够画出 Collection / List / Set / Queue / Map 的基本关系。
  • 能够解释为什么 Map 不继承 Collection
  • 能够分别举出 List、Set、Map 的一个真实业务例子。
  • 能够解释数组与集合至少 4 个区别。
  • 能够解释集合为什么需要泛型。
  • 能够解释接口与实现类之间的关系。
  • 能够说明 JDK 21 SequencedCollection 的基本定位。
  • 面对一个业务数据需求,能够先分析数据特征,再选择集合,而不是直接默认使用 ArrayList