Appearance
2026-08-29 · 集合是什么、分几类、怎么遍历——集合体系的地图,先看这篇再碰具体实现。
Java 集合框架
1. 为什么需要集合
数组有一个硬伤:长度固定。存 10 个元素就得 new int[10],来了第 11 个就装不下,只能新建更大的数组再拷过去。
集合(Collection)就是来解决这个问题的容器:
| 对比 | 数组 | 集合 |
|---|---|---|
| 长度 | 固定,不可变 | 可变,自动扩容 |
| 存啥 | 只能存一种类型 | 可存任意对象类型 |
| 增删 | 手动移位,麻烦 | add() / remove() 一步完成 |
| 常用方法 | 几乎没有 | 封装好了查找、判断、遍历 |
实际开发里集合的使用频率远高于数组——数据库查出来的多条记录、前端传来的列表,全部用集合装。
2. 集合的两大体系
Java 的集合分两大体系,先记住这张图(后面所有类都在这个框架里):
Collection 接口(单列集合:一个一个地存)
├── List 接口(有序、可重复)
│ ├── ArrayList ← 底层数组,查快增删慢
│ └── LinkedList ← 底层链表,增删快查慢
│
└── Set 接口(无序、不可重复)
├── HashSet ← 底层哈希表,去重
├── LinkedHashSet ← 哈希表+链表,去重且保序
└── TreeSet ← 底层二叉树,去重且排序
Map 接口(双列集合:键值对 key-value 地存)
├── HashMap ← 底层哈希表(最常见)
├── LinkedHashMap ← 哈希表+链表,保插入顺序
└── TreeMap ← 底层二叉树,按键排序一句话区分:
- Collection = 单列集合,存的是"一个个的值"(比如一串用户名)
- Map = 双列集合,存的是"键值对"(比如 学号→学生,一个 key 对应一个 value)
Map 不继承 Collection,它是另一棵树,但统称"集合"。集合框架两大体系就是 Collection 和 Map。
3. Collection 接口的通用方法
Collection 是 List 和 Set 的父接口,所有实现类都有的方法:
java
import java.util.*;
Collection<String> c = new ArrayList<>();
c.add("张三"); // 添加元素
c.add("李四");
c.remove("张三"); // 删除元素
c.contains("李四"); // 是否包含 → true
c.size(); // 元素个数 → 1
c.isEmpty(); // 是否为空 → false
c.clear(); // 清空所有元素用
Collection类型接收ArrayList对象,这叫面向接口编程——只关心"这是个集合",不关心底层是数组还是链表。换实现类,代码不用改。
4. 泛型:集合的标配
不加泛型,集合里什么都能放,但取出来全是 Object,用一次强转一次:
java
// ❌ 不用泛型:取出必须强转,还可能 ClassCastException
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 强转,麻烦
// ✅ 用泛型:类型写死在声明里,取出直接是 String
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 不用转了泛型的作用:编译期就拦住类型错误。 往 List<String> 里 add 一个数字,编译直接报错,不用等运行期出问题。
泛型(Generic)从 Java 5 开始有,是集合框架的标配写法。看到
<>尖括号,就是泛型——声明时写一次类型,全程受益。
5. 迭代器 Iterator:遍历的底层机制
5.1 为什么不能只用 for 循环
普通的 for 循环有个问题——一边遍历一边删除会出问题:
java
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
// ❌ 边遍历边删:会抛 ConcurrentModificationException
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("b")) {
list.remove(i); // 删元素的同时 size 变了,索引错位
}
}5.2 Iterator 的正确姿势
java
Iterator<String> it = list.iterator(); // 拿到迭代器
while (it.hasNext()) { // 还有下一个吗?
String s = it.next(); // 取下一个
if (s.equals("b")) {
it.remove(); // ✅ 用迭代器的 remove,安全
}
}迭代器的工作原理(想象成"游标"):
[a] → [b] → [c]
↑
it 最初指向第一个元素前面
hasNext() 问:后面还有吗?→ true
next() 取走 b,游标后移一位
remove() 删掉刚取走的那个5.3 增强 for(foreach)就是迭代器
java
for (String s : list) {
System.out.println(s);
}增强 for 是迭代器的语法糖,只读遍历够用;但增强 for 里不能 remove(底层还是迭代器,删了同样报错)。要删就用 it.remove()。
集合遍历三选一:只读 → 增强 for(最简洁);要删除 → 迭代器;要下标 → 普通 for。
6. 各集合的底层数据结构速查
不同集合性能不同,根源在底层数据结构不同。四种结构一句话:
| 数据结构 | 一句话 | 代表集合 | 特点 |
|---|---|---|---|
| 数组 | 连续内存、按下标直取 | ArrayList | 查快(O(1))、增删慢 |
| 链表 | 每个节点存值和下一个的地址 | LinkedList | 增删快、查慢 |
| 哈希表 | 按哈希值直接定位桶 | HashSet / HashMap | 查找极快、无序 |
| 二叉树 | 左小右大的树 | TreeSet / TreeMap | 自动排序 |
怎么选(实际开发判断标准):
- 存一批数据按顺序取、按下标取 →
ArrayList - 频繁在中间插入/删除 →
LinkedList - 要"去重" →
Set系列 - 要"按 key 查 value"(缓存、映射)→
HashMap - 要排序 →
TreeSet/TreeMap
记住一句话:90% 的 List 场景用 ArrayList,90% 的 Map 场景用 HashMap。 其余的是特定需求才换。
7. 本篇小结
- 集合 = 可变长度的容器,实际开发比数组常用
- 两大体系:Collection(单列:List/Set) 和 Map(双列:键值对)
- Collection 通用方法:add/remove/contains/size/isEmpty/clear
- 泛型声明类型,编译期拦住类型错误,
List<String>标配写法 - 遍历:只读用增强 for;要删除用迭代器
it.remove() - 底层数据结构决定性能:数组查快、链表增删快、哈希查找极快、二叉树自动排序