# 20260803 课堂笔记 — 比较器排序(Comparable 自然排序 / Comparator 比较器排序)+ 集合工具类 Collections + 20260731 课后作业解析 - **日期**:2026-08-03 - **项目**:`c260803` - **包路径**:`course` / `homework0731` - **作者**:WanJL --- ## 目录 1. [比较器概述:TreeSet / TreeMap 与两种排序方式](#1-比较器概述treeset--treemap-与两种排序方式) 2. [自然排序 Comparable(实体类内建排序规则)](#2-自然排序-comparable实体类内建排序规则) 3. [比较器排序 Comparator(临时自定义排序)](#3-比较器排序-comparator临时自定义排序) 4. [返回值规则与 compareTo/equals 一致性的官方推荐](#4-返回值规则与-comparetoequals-一致性的官方推荐) 5. [集合工具类 Collections](#5-集合工具类-collections) 6. [20260731 课后作业解析:Map 集合基本操作与遍历](#6-20260731-课后作业解析map-集合基本操作与遍历) 7. [20260731 课后作业解析:异常处理综合(自定义异常)](#7-20260731-课后作业解析异常处理综合自定义异常) 8. [20260731 课后作业解析:匿名内部类 Comparator 排序](#8-20260731-课后作业解析匿名内部类-comparator-排序) 9. [随堂练习要点](#9-随堂练习要点) 10. [拓展阅读](#10-拓展阅读) --- ## 1. 比较器概述:TreeSet / TreeMap 与两种排序方式 ### 概念 之前学过的 **HashMap / HashSet 是无序集合**——取出的顺序和存入的顺序**不一致**(底层根据 hashCode 计算存储位置)。 而 **TreeMap / TreeSet 是有序集合**——取出的顺序和存入的顺序一致(注意:其实也不一定完全一致,而是**排序过**的)。 > ⚠️ 不是所有类型(包括自定义类)都能直接存入 TreeSet 或 TreeMap,是有条件的: > 1. 该类型**实现了 `Comparable` 接口**并重写了 `compareTo(T o)` 方法(自然排序); > 2. 或者**在创建集合对象时,在构造方法中传入比较器对象 `Comparator`**,进行临时的自定义排序(比较器排序)。 ### 两种排序方式的定位 | 排序方式 | 实现方式 | 使用场景 | |----------|----------|----------| | **自然排序(Comparable)** | 实体类实现 `Comparable` 接口,重写 `compareTo(T o)` | 默认排序方式,类设计期间就确定好的排序规则 | | **比较器排序(Comparator)** | 创建集合对象时传入 `Comparator` 接口实现类,重写 `compare()` | 类定义期间没实现 Comparable,或临时需要其他方式排序 | **使用原则**:一般还是以**实现 Comparable 接口为主**;只有在类定义期间没有实现 `Comparable` 接口,或者加入集合时**需要临时做其他方式的排序**,才会使用 `Comparator` 进行临时自定义排序。 ### 一个完整实体类的结构回顾 ```java // 来源:course/Demo01.java(注释部分) public class 类名 implements Comparable<类名>{ private 类型 属性1; private 类型 属性2; ... private 类型 属性n; 无参构造方法 有参构造方法(参数...){ } // 可选:内部类、代码块... getter方法... setter方法... equals()方法... hashCode()方法... toString()方法... compareTo(T o)方法... } ``` --- ## 2. 自然排序 Comparable(实体类内建排序规则) ### 概念 **自然排序**:实体类实现 `Comparable` 接口,重写 `compareTo(T o)` 方法,根据**返回值**决定排序规则。这是类的**默认排序方式**。 ```java // 来源:course/Student.java package course; public class Student implements Comparable{ private String name; private int age; private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } // getter / setter ...(略) // toString() ...(略) @Override public int compareTo(Student o) { // 设置按照年龄排序(从小到大) int result = this.age - o.getAge(); // 年龄相同 → 再按姓名排序(二次比较,保证唯一性) return result == 0 ? this.name.compareTo(o.getName()) : result; } } ``` ### 关键理解 1. **比较的就是「当前对象 this」与「传入对象 o」**:`this.age - o.getAge()` 正负决定了当前对象比传入对象大还是小。 2. **二次比较**:只用年龄排序时,若两名学生年龄相同(返回值 0),后一个会因「重复」而**无法存入** TreeSet。所以排序依据最好唯一,或再叠加其他属性比较(如姓名)。 --- ## 3. 比较器排序 Comparator(临时自定义排序) ### 概念 **比较器排序**:创建集合对象的时候,传入 `Comparator` 接口的实现类对象,重写 `compare()` 方法,根据返回值进行排序。**不会修改实体类本身**,是临时的、自定义的排序规则。 通常配合**匿名内部类**使用,实现 `Comparator` 并重写 `compare(Student o1, Student o2)`。 ```java // 来源:course/Demo01.java(部分) package course; import java.util.Comparator; import java.util.TreeSet; public class Demo01 { public static void main(String[] args) { // 按照自定义的要求临时进行排序:先按成绩升序,成绩相同再按姓名排序 TreeSet set = new TreeSet<>(new Comparator() { @Override public int compare(Student o1, Student o2) { int result = (int) (o1.getScore() - o2.getScore()); return result == 0 ? o1.getName().compareTo(o2.getName()) : result; } }); set.add(new Student("张三", 7, 85)); set.add(new Student("李四", 2, 90)); set.add(new Student("王五", 20, 78)); set.add(new Student("赵六", 222, 88)); for (Student s : set) { System.out.println(s); } } } ``` ```java // 来源:course/Demo01.java(部分) // 不传 Comparator → 使用 Student 类自带的自然排序(Comparable:按年龄) TreeSet treeSet = new TreeSet<>(); // 默认使用自然排序 treeSet.add(new Student("张三", 20, 85)); treeSet.add(new Student("李四", 22, 90)); treeSet.add(new Student("王五", 20, 78)); // 年龄 20 与"张三"相同 → 触发 name 二次比较 treeSet.add(new Student("赵六", 21, 88)); for (Student s : treeSet) { System.out.println(s); } ``` ### 关键理解 1. **构造方法传 Comparator = 覆盖默认排序**:`new TreeSet<>(comparator)` 用的是比较器排序;`new TreeSet<>()` 用的是自然排序。 2. **Comparator 不改动实体类**:排序规则写在集合这边,适合「类已定稿、临时按别的规则排序」的场景。 3. **匿名内部类**是 Comparator 最常用的实现方式(后续 Collections.sort 也用它)。 --- ## 4. 返回值规则与 compareTo/equals 一致性的官方推荐 ### 两种方式共用的返回值规则 | 返回值 | 含义 | 处理结果 | |--------|------|----------| | **负数** | 当前存入的元素比较小 | **存左边** | | **0** | 当前存入的元素和已有元素「重复」 | **不存**(TreeSet 去重 / TreeMap 键去重) | | **正数** | 当前存入的元素比较大 | **存右边** | ### 为什么官方强烈推荐「compareTo == 0 时 equals 也返回 true」? 因为这样能**更好地存入 TreeMap / TreeSet 集合**: - 如果我们设置的是**根据年龄排序**(或其他某个属性),那么当把这些对象存入 TreeMap/TreeSet 时,**会因为某个属性相同导致对象存入不进集合**。 - 官方建议:**如果 `x1.compareTo(x2) == 0` 或 `compare(x1, x2) == 0`,那么建议 `x1.equals(x2)` 返回 `true`,并且 `hashCode` 一致。** 这样排序判定、相等判定、哈希判定三者保持一致,集合的「去重」行为才符合预期。 --- ## 5. 集合工具类 Collections ### 概念 `java.util.Collections` 是**操作集合的工具类**,专门用来对 `List` / `Set` / `Map` 等集合进行排序、查找、填充、转换等操作。 - 工具类里的方法**全部是静态方法**,**不需要创建对象**,直接通过类名调用 - 类比记忆:`Arrays` 是**数组**的工具类,`Collections` 就是**集合**的工具类 - 今天重点:`Collections.sort` 是**比较器排序**在 List 上的应用——传入 `Comparator` 匿名内部类即可对 List 自定义排序(与 TreeSet 构造传 Comparator 思路一致) ### 快速创建集合:List.of() ```java // 来源:course/Demo02.java(部分) // List.of() 是 JDK9+ 提供的快速创建【不可变集合】的方法 // 再用 new ArrayList<>(...) 包装成可变的 ArrayList List list = new ArrayList<>(List.of("C", "B", "A", "F", "G")); ``` ### 代码示例(Collections 常用方法) ```java // 来源:course/Demo02.java package course; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Demo02 { public static void main(String[] args) { List list = new ArrayList<>(List.of("C", "B", "A", "F", "G")); // 1. 排序:自然顺序升序——要求每个元素都必须实现 Comparable 接口,重写 compareTo() Collections.sort(list); // 2. 排序:自定义方式排序——Comparator 匿名内部类,重写 compare() Collections.sort(list, new Comparator() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // 3. 逆序:把集合元素顺序反转 Collections.reverse(list); // 4. 查找:二分查找指定元素,存在返回索引,不存在返回 -1 int i = Collections.binarySearch(list, "C"); // 5. 查找最大 / 最小元素 String max = Collections.max(list); String min = Collections.min(list); // 6. 把当前集合转化为不可变集合(不能 add / remove) List unmodifiableList = Collections.unmodifiableList(list); // 7. 随机打乱集合元素顺序 Collections.shuffle(list); // 8. 使用某个对象全部填充元素 Collections.fill(list, "X"); // 9. 交换两个位置的元素 Collections.swap(list, 0, 1); // 10. 旋转元素:整体向后移动 distance 个位置 Collections.rotate(list, 2); // 11. 替换指定元素:把旧值全部换成新值 Collections.replaceAll(list, "A", "Z"); } } ``` ### Collections 常用方法速查 | 方法 | 作用 | 说明 | |------|------|------| | `sort(List)` | 自然升序排序 | 元素**必须实现 Comparable**,否则抛 ClassCastException | | `sort(List, Comparator)` | 自定义排序 | 与 TreeSet(Comparator) 思路一致,按 compare 返回值排 | | `reverse(List)` | 逆序 | 把集合元素顺序整体反转 | | `binarySearch(List, key)` | 二分查找 | 返回元素索引,不存在返回 -1;**查找前必须先排序** | | `max(List)` / `min(List)` | 最大 / 最小元素 | 元素须可比较(实现 Comparable) | | `unmodifiableList(List)` | 转不可变集合 | 返回的集合不能增删改,否则抛 UnsupportedOperationException | | `shuffle(List)` | 随机打乱 | 类似洗牌 | | `fill(List, obj)` | 全部填充 | 用指定对象替换集合中所有元素 | | `swap(List, i, j)` | 交换元素 | 交换两个索引位置的元素 | | `rotate(List, distance)` | 旋转元素 | 元素整体向后移动 distance 个位置 | | `replaceAll(List, oldVal, newVal)` | 替换元素 | 把所有 oldVal 替换为 newVal | ### 关键理解 1. **Collections 与比较器的关系**:`Collections.sort(list, comparator)` 把**比较器排序**从「集合存入时」扩展到了「已有 List 排序」——二者共用同一套返回值规则(负数存左 / 0 相等 / 正数存右)。 2. **`sort(List)` 的前提**:自然排序要求元素实现 `Comparable`,否则运行时报 `ClassCastException`——与 TreeSet/TreeMap 存入对象的要求**完全一致**。 3. **`binarySearch` 使用前提**:二分查找基于有序数组,所以**查找前必须先 sort**,否则结果不可靠;找不到返回 -1。 4. **`List.of()` 的陷阱**:返回的是**不可变集合**,直接 `add`/`remove` 会抛 `UnsupportedOperationException`;Demo02 用 `new ArrayList<>(List.of(...))` 包装后才可变。 --- ## 6. 20260731 课后作业解析:Map 集合基本操作与遍历 ### 作业要求回顾 > 创建 `HashMap` 保存学号-成绩,练习 `put/size/get/containsKey/remove` 等基本操作;分别用 `keySet + get` 和 `entrySet` 两种方式遍历;再创建 `TreeMap` 观察按键自动排序的效果。 ### 代码解析 ```java // 来源:homework0731/p2_map/MapDemo.java package homework0731.p2_map; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; public class MapDemo { public static void main(String[] args) { // 1. 创建 HashMap,添加 4 组成绩 HashMap hashMap = new HashMap<>(); hashMap.put("x001", 90); hashMap.put("x002", 85); hashMap.put("x003", 92); hashMap.put("x004", 78); // 2. 输出集合大小 int size = hashMap.size(); // 学生人数:4 // 3. 查询 / 判断键是否存在 // get("x002") → 85 // containsKey("x001") → true,containsKey("x999") → false // 4. 修改:put("x004", 88)(键已存在 → 覆盖旧值) // 5. 删除:remove("x001") // 6. 遍历方式二:entrySet() + getKey()/getValue() Set> entries = hashMap.entrySet(); for (Map.Entry entry : entries) { System.out.println(entry.getKey() + "--->" + entry.getValue()); } // 7. TreeMap:放入同样的 4 组数据,按键(学号)自动排序 TreeMap treeMap = new TreeMap<>(); treeMap.put("x001", 90); treeMap.put("x002", 85); treeMap.put("x003", 92); treeMap.put("x004", 78); Set set = treeMap.keySet(); for (String key : set) { Integer value = treeMap.get(key); System.out.println(key + "--->" + value); } } } ``` ### 知识点总结 | 知识点 | 说明 | |--------|------| | `put(key, value)` | 添加键值对;**键已存在时覆盖旧值**(即修改) | | `size()` / `get(key)` | 集合大小 / 根据键取值 | | `containsKey(key)` | 判断键是否存在 | | `remove(key)` | 根据键删除键值对 | | **keySet() 遍历** | 先取所有键(`Set`),再 `get(key)` 反查值 | | **entrySet() 遍历** | 一次性拿到键值对 Entry,`getKey()/getValue()` 直接取值,效率更高 | | **TreeMap** | **按键自动排序**(学号 x001→x004 按字典序输出),与 HashMap 无序形成对比 | --- ## 7. 20260731 课后作业解析:异常处理综合(自定义异常) ### 作业要求回顾 > 循环输入 5 个成绩(0~100),用 try-catch 处理异常:输入非整数 → `InputMismatchException`;成绩不在 0~100 → 抛出自定义 `InvalidScoreException`;最后用迭代器遍历输出成绩。 ### 自定义异常类 ```java // 来源:homework0731/p4_exception/InvalidScoreException.java package homework0731.p4_exception; // 自定义运行时异常:继承 RuntimeException,构造方法传入错误信息 public class InvalidScoreException extends RuntimeException { public InvalidScoreException(String message) { super(message); } } ``` ### 主程序解析 ```java // 来源:homework0731/p4_exception/ScoreStatisticsDemo.java package homework0731.p4_exception; import java.util.ArrayList; import java.util.InputMismatchException; import java.util.Iterator; import java.util.Scanner; public class ScoreStatisticsDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); ArrayList scores = new ArrayList<>(); // 循环输入 5 个合法成绩;i 只有输入成功才自增(保证最终拿到 5 个合法成绩) for (int i = 0; i < 5; ) { try { int s = sc.nextInt(); if (s < 0 || s > 100) { throw new InvalidScoreException("成绩必须在0~100之间"); // 主动抛出自定义异常 } scores.add(s); i++; } catch (InputMismatchException e) { // 输入的不是整数 System.out.println("请输入整数"); sc.nextLine(); // 清空缓冲区,避免死循环 } catch (InvalidScoreException e) { // 成绩越界 System.out.println("成绩不合法:成绩必须在0~100之间"); } } // 使用迭代器遍历输出所有成绩 Iterator iterator = scores.iterator(); while (iterator.hasNext()) { Integer i = iterator.next(); System.out.print(i + ", "); } } } ``` ### 知识点总结 | 知识点 | 说明 | |--------|------| | **自定义异常** | 继承 `RuntimeException`(运行时异常)即可,通过构造方法传入 `message` | | **throw 主动抛出** | `throw new InvalidScoreException("...")` 在业务逻辑中主动制造异常 | | **多 catch 块** | 一个 try 可配多个 catch,分别捕获 `InputMismatchException` 和自定义异常 | | **`sc.nextLine()` 清空缓冲** | 捕获 `InputMismatchException` 后必须清空输入缓冲,否则会陷入死循环 | | **循环内 i++ 放在 try 内** | 只有输入合法才自增计数,保证最终得到 5 个合法成绩(程序更鲁棒) | --- ## 8. 20260731 课后作业解析:匿名内部类 Comparator 排序 ### 作业要求回顾 > 创建 `ArrayList`,用**匿名内部类**实现 `Comparator` 分别按年龄升序、成绩降序排序(`Collections.sort`);再创建 `TreeSet` 传入按姓名排序的匿名 Comparator,观察自动排序。 ### 代码解析 ```java // 来源:homework0731/p5_sort/SortDemo.java package homework0731.p5_sort; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class SortDemo { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add(new Student("张三", 20, 85)); list.add(new Student("李四", 22, 90)); list.add(new Student("王五", 19, 78)); list.add(new Student("赵六", 21, 88)); // 1. 匿名内部类实现 Comparator,按年龄升序排序 Collections.sort(list, new Comparator() { @Override public int compare(Student s1, Student s2) { return s1.getAge() - s2.getAge(); // 年龄升序 } }); for (Student s : list) { System.out.println(s); } // 2. 匿名内部类 Comparator,按成绩(double)降序排序 // 注意:double 不能直接相减强转,用 Double.compare 最安全 Collections.sort(list, new Comparator() { @Override public int compare(Student s1, Student s2) { return Double.compare(s2.getScore(), s1.getScore()); // 成绩降序 } }); // 3. 创建 TreeSet,传入按姓名排序的匿名 Comparator,迭代器遍历观察自动排序 // TreeSet treeSet = new TreeSet<>(comparator); // 添加学生后自动按姓名排序 } } ``` ```java // 来源:homework0731/p5_sort/Student.java(普通实体类,未实现 Comparable) package homework0731.p5_sort; public class Student { private String name; private int age; private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } // getter / setter ...(略) // toString() ...(略) } ``` ### 知识点总结 | 知识点 | 说明 | |--------|------| | **`Collections.sort(list, comparator)`** | 对 List 集合用比较器排序(原地排序,改变原集合) | | **匿名内部类 Comparator** | 临时指定排序规则,无需修改 Student 类(本例 Student 未实现 Comparable) | | **`Double.compare(a, b)`** | double 类型比较的正确姿势:浮点数相减精度丢失,且无法强转 int;用 `Double.compare` 最安全 | | **Comparator 降序技巧** | 交换 `compare` 中两个参数的位置即可反向:`compare(s2, s1)` = 降序 | | **TreeSet + Comparator** | 无参构造用自然排序;传入 Comparator 的构造用比较器排序,存入即自动排序 | --- ## 9. 随堂练习要点 ### 练习一:自然排序(Comparable)与比较器排序(Comparator)对比 **核心步骤**: 1. `Student` 类实现 `Comparable`,`compareTo` 按年龄升序(年龄相同再按姓名) 2. `new TreeSet<>()` 存入 4 名学生 → 观察自然排序结果 3. `new TreeSet<>(new Comparator() {...})` 按成绩升序 → 观察比较器排序结果 4. 对比两种排序的输出顺序 **思考**: 1. 为什么年龄相同的两名学生会「丢失」一个?—— `compareTo` 返回 0 视为重复,不存入;所以排序依据最好唯一,或叠加二次比较(姓名) 2. 什么时候用 Comparable,什么时候用 Comparator?—— 类设计期确定默认规则用 Comparable;类已定稿需临时换规则用 Comparator ### 练习二:Map 集合基本操作与遍历(作业一) **核心步骤**: 1. `HashMap` 的 `put/size/get/containsKey/remove` 五类操作 2. `keySet() + get()` 遍历;`entrySet() + getKey()/getValue()` 遍历 3. `TreeMap` 用同样数据验证按键自动排序 **思考**: 1. `put` 键已存在时是「添加」还是「修改」?—— 覆盖旧值,等价于修改 2. keySet 遍历和 entrySet 遍历哪个效率高?—— entrySet 一次拿到键和值;keySet 还要 `get(key)` 反查一次 ### 练习三:异常处理综合(作业二) **核心步骤**: 1. 自定义 `InvalidScoreException extends RuntimeException` 2. 输入循环内 try-catch 多 catch 块(`InputMismatchException` / `InvalidScoreException`) 3. 迭代器遍历输出成绩 **思考**: 1. 捕获 `InputMismatchException` 后为什么要 `sc.nextLine()`?—— 清空输入缓冲区,否则错误输入会反复触发异常形成死循环 2. 自定义异常继承 `RuntimeException` 和 `Exception` 有何区别?—— RuntimeException 是运行时异常,方法内抛出可不用显式声明 throws,编译不强制处理 ### 练习四:匿名内部类 Comparator 排序(作业三) **核心步骤**: 1. `Collections.sort(list, new Comparator(){...})` 按年龄升序 2. 按成绩降序:`Double.compare(s2.getScore(), s1.getScore())` 3. `TreeSet` 传入按姓名排序的匿名 Comparator,迭代器遍历 **思考**: 1. double 成绩比较为什么不用 `(int)(s1.getScore()-s2.getScore())`?—— double 相减可能有精度误差,强转 int 也不安全;`Double.compare` 是标准做法 2. 降序为什么交换参数位置即可?—— compare 返回值符号取反,排序方向随之反转 ### 练习五:Collections 工具类常用方法 **核心步骤**: 1. 用 `new ArrayList<>(List.of("C","B","A","F","G"))` 快速创建可变 List 2. `Collections.sort(list)` 自然排序 → 观察字母升序 3. `Collections.sort(list, new Comparator(){...})` 自定义排序(如降序) 4. `Collections.reverse(list)` / `Collections.shuffle(list)` 观察顺序变化 5. `Collections.binarySearch(list, "C")`(先排序再查找)、`Collections.max/min(list)` 6. `Collections.unmodifiableList(list)` 转不可变集合后尝试 `add()`,观察异常 7. `Collections.fill(list, "X")` / `Collections.swap(list, 0, 1)` / `Collections.replaceAll(list, "A", "Z")` **思考**: 1. `Collections.sort(list)` 为什么要求元素实现 `Comparable`?不实现会怎样?—— 工具类内部要比较元素大小,不实现则运行时抛 `ClassCastException` 2. `binarySearch` 查找前为什么要先排序?—— 二分查找基于有序序列,未排序结果不可靠 3. `List.of()` 返回的集合为什么不能 `add/remove`?—— 返回的是不可变集合,直接修改抛 `UnsupportedOperationException`,需用 `new ArrayList<>(...)` 包装 --- ## 10. 拓展阅读 ### 官方文档 - [Comparable 接口 (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Comparable.html) - [Comparator 接口 (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Comparator.html) - [TreeSet (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/TreeSet.html) - [TreeMap (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/TreeMap.html) - [Collections 工具类 (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Collections.html) - [List.of() (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/List.html#of()) - [RuntimeException (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/RuntimeException.html) ### 推荐阅读 - 《Java 核心技术 卷 I》第 9 章 集合 —— 排序与比较器 - 《Effective Java》第 3 版 第 14 条:考虑实现 Comparable 接口 - 《Java 编程思想》第 17 章 容器深入研究 —— 哈希码与排序 ### 相关知识点 | 前置知识 | 当前知识 | 后续知识 | |----------|----------|----------| | 匿名内部类(0727) | Comparator 匿名内部类实现 | Lambda 表达式(函数式接口) | | 集合体系与泛型(0729) | TreeSet / TreeMap 有序集合 | 红黑树底层(0731 已讲) | | HashMap 基本操作(0731) | Map 综合练习(keySet/entrySet 遍历) | Map 与 Set 的互转、分组统计 | | 异常处理(0728) | 自定义异常 + 多 catch 块 | 异常链、try-with-resources | | Arrays 工具类(0716) | Collections 集合工具类 | Stream API、Java 8 新特性 | | Comparator 比较器排序(本节 3) | Collections.sort(list, comparator) | 函数式接口 & Lambda 简化比较器 |