【Java 集合实战】从基础到进阶:多重集合 / 缓存 / 队列 / 栈核心知识点总结
最近系统学习了 Java 集合相关的实战场景,涵盖缓存模拟、队列栈实现、有序集合 / 多重集合维护等核心场景,整理了从基础语法到进阶用法的全知识点,适合新手查漏补缺~
一、基础语法:变量 / 方法 / 作用域核心规则
1.1 大小写区分:基本类型 vs 类 / 接口
| 类型分类 | 命名规则 | 示例 | 核心说明 |
|---|---|---|---|
| 基本数据类型 | 全小写 | int/char/boolean | Java 底层值类型,直接存储简单数据 |
| 类 / 接口 | 大驼峰 | Stack/TreeSet/TreeMap | 引用类型,需new创建对象才能使用 |
| 包装类 | 首字母大写 | Integer/Character | 基本类型的 “类版本”,可存入集合 |
核心区别:
- 基本类型(小写):像 “螺丝钉”,基础且直接用;
- 类 / 接口(大写):像 “工具箱”,需组装(new)后使用。
1.2 作用域规则:局部变量 vs 静态成员变量
public class Main {
// 静态成员变量:整个类的方法都能访问
private static TreeMap<Integer, Integer> map = new TreeMap<>();
public static void main(String[] args) {
// 局部变量:仅main方法内可访问,其他方法(如insertValue)无法直接使用
int num = 10;
}
}
易错点:不要在 main 方法内定义需要被其他静态方法访问的变量,需提升为类的静态成员变量。
1.3 循环控制语句:continue vs break
| 语句 | 核心作用 | 示例场景 |
|---|---|---|
| continue | 跳过当前轮循环剩余代码,直接进入下一轮 | 缓存命中时,跳过后续更新逻辑 |
| break | 终止整个循环,后续轮次不再执行 | 找到目标元素后,终止遍历 |
示例:
// 打印1-5的奇数(用continue跳过偶数)
for (int i = 1; i <= 5; i++) {
if (i % 2 == 0) continue; // 偶数直接跳过,不执行打印
System.out.println(i); // 仅打印1/3/5
}
二、集合核心 API:从基础到进阶
2.1 基础集合:Queue/Stack/HashSet
| 集合类型 | 核心特性 | 常用 API | 典型场景 |
|---|---|---|---|
| Queue(队列) | 先进先出(FIFO) | offer()/poll()/isEmpty() | 缓存淘汰(最早进入先删) |
| Stack(栈) | 后进先出(LIFO) | push()/pop()/size() | 用两个栈实现队列 |
| HashSet | 无序、元素唯一 | add()/remove()/contains() | 快速判断元素是否存在 |
实战场景:用 Queue+HashSet 模拟缓存
Queue<Integer> cacheQueue = new LinkedList<>(); // 记录缓存顺序
Set<Integer> cacheSet = new HashSet<>(); // 快速判断命中
int missCount = 0; // 缓存未命中次数
// 缓存命中逻辑
if (cacheSet.contains(word)) {
continue; // 命中则跳过后续逻辑
}
missCount++; // 未命中,次数+1
2.2 有序集合:TreeSet/TreeMap(红黑树底层)
2.2.1 核心优势
- 有序性:底层红黑树保证元素按大小排序,支持前驱 / 后继查询;
- 高效性:插入 / 删除 / 查询均为 O (logn),适配 n≤10⁵的大规模数据。
2.2.2 TreeMap 核心 API(多重集合核心)
| API 方法 | 作用 | 对应场景 |
|---|---|---|
| getOrDefault(x, 0) | 查 x 的值,无则返回默认值 0 | 统计元素重复次数 |
| put(x, value) | 存入键值对,覆盖已有值 | 更新元素重复次数 |
| lowerKey(x) | 返回小于 x 的最大键(前驱) | 找前驱元素 |
| higherKey(x) | 返回大于 x 的最小键(后继) | 找后继元素 |
| containsKey(x) | 判断键 x 是否存在 | 删除元素前的存在性检查 |
核心语法:map.put(x, map.getOrDefault(x, 0) + 1)
- 拆解逻辑:
-
- map.getOrDefault(x, 0):查 x 的当前次数,无则返回 0(default= 兜底值);
-
- +1:插入一个 x,次数 + 1;
-
- put(x, ...):将新次数存入 map。
- 等价手写逻辑(更易理解):
if (map.containsKey(x)) {
int count = map.get(x);
map.put(x, count + 1);
} else {
map.put(x, 1);
}
三、实战场景:完整业务实现
3.1 场景 1:机器翻译缓存模拟
需求:维护固定容量缓存,统计未命中次数(淘汰最早进入的元素)。
核心逻辑:
- Queue 记录缓存顺序,保证 “先进先出” 淘汰;
- HashSet 快速判断缓存命中,避免遍历 Queue;
- 缓存满时,删除 Queue 队首元素,同时从 HashSet 移除。
3.2 场景 2:多重集合维护
需求:支持插入重复元素、删除单个元素、查次数、找前驱 / 后继。
核心实现:
// 全局变量:TreeMap(键=元素值,值=重复次数)+ 总元素数
private static TreeMap<Integer, Integer> map = new TreeMap<>();
private static int totalSize = 0;
// 1. 插入元素
public static void insertValue(int x) {
map.put(x, map.getOrDefault(x, 0) + 1);
totalSize++;
}
// 2. 删除单个元素
public static void eraseValue(int x) {
if (!map.containsKey(x)) return;
int count = map.get(x);
if (count == 1) map.remove(x);
else map.put(x, count - 1);
totalSize--;
}
// 3. 找前驱(小于x的最大数)
public static int getPre(int x) {
Integer preKey = map.lowerKey(x);
return preKey == null ? -1 : preKey;
}
3.3 场景 3:两个栈实现队列
核心逻辑:
- 栈 1(stack1):负责入队(push),新元素直接压入;
- 栈 2(stack2):负责出队(pop),为空时将 stack1 元素全部转移过来(反转顺序)。
public int pop() {
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) stack2.push(stack1.pop());
}
return stack2.pop();
}
四、核心易错点 & 避坑指南
- 空指针异常:
-
- 不要直接用int count = map.get(x),若 x 不存在会返回 null,拆箱报错;
-
- 优先用map.getOrDefault(x, 0)兜底。
- 集合选择错误:
-
- 需有序 / 找前驱后继 → 用 TreeSet/TreeMap(红黑树);
-
- 仅需快速判重 → 用 HashSet;
-
- 需记录顺序 → 用 Queue/LinkedList。
- 性能问题:
-
- 统计总元素数时,用全局变量totalSize,避免遍历 TreeMap 求和;
-
- 大规模数据(n≥10⁴)避免用 List 遍历判重(O (n)),优先用 Set(O (1))。
五、知识点关联:底层逻辑
- 红黑树:TreeSet/TreeMap 的底层实现,自平衡二叉查找树,保证插入 / 查询 / 删除效率为 O (logn);
- 哈希表:HashSet/HashMap 的底层实现,查询效率 O (1),但无序,无法找前驱 / 后继;
- LRU 缓存思想:Queue 的 “先进先出” 特性,对应缓存淘汰策略中 “最早进入先淘汰”。
写在最后:Java 集合的核心是 “选对结构 + 用对 API”,新手先掌握每种集合的核心特性和典型场景,再通过实战理解底层逻辑,就能少踩坑、写高效代码~