|
|
@@ -0,0 +1,495 @@
|
|
|
+# 20260803 课堂笔记 — 比较器排序(Comparable 自然排序 / Comparator 比较器排序)+ 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. [20260731 课后作业解析:Map 集合基本操作与遍历](#5-20260731-课后作业解析map-集合基本操作与遍历)
|
|
|
+6. [20260731 课后作业解析:异常处理综合(自定义异常)](#6-20260731-课后作业解析异常处理综合自定义异常)
|
|
|
+7. [20260731 课后作业解析:匿名内部类 Comparator 排序](#7-20260731-课后作业解析匿名内部类-comparator-排序)
|
|
|
+8. [随堂练习要点](#8-随堂练习要点)
|
|
|
+9. [拓展阅读](#9-拓展阅读)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 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<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() ...(略)
|
|
|
+
|
|
|
+ @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<Student>` 并重写 `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<Student> set = new TreeSet<>(new Comparator<Student>() {
|
|
|
+ @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<Student> 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. 20260731 课后作业解析:Map 集合基本操作与遍历
|
|
|
+
|
|
|
+### 作业要求回顾
|
|
|
+
|
|
|
+> 创建 `HashMap<String, Integer>` 保存学号-成绩,练习 `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<String, Integer> 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<Map.Entry<String, Integer>> entries = hashMap.entrySet();
|
|
|
+ for (Map.Entry<String, Integer> entry : entries) {
|
|
|
+ System.out.println(entry.getKey() + "--->" + entry.getValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. TreeMap:放入同样的 4 组数据,按键(学号)自动排序
|
|
|
+ TreeMap<String, Integer> treeMap = new TreeMap<>();
|
|
|
+ treeMap.put("x001", 90);
|
|
|
+ treeMap.put("x002", 85);
|
|
|
+ treeMap.put("x003", 92);
|
|
|
+ treeMap.put("x004", 78);
|
|
|
+
|
|
|
+ Set<String> 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<K>`),再 `get(key)` 反查值 |
|
|
|
+| **entrySet() 遍历** | 一次性拿到键值对 Entry,`getKey()/getValue()` 直接取值,效率更高 |
|
|
|
+| **TreeMap** | **按键自动排序**(学号 x001→x004 按字典序输出),与 HashMap 无序形成对比 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 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<Integer> 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<Integer> 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 个合法成绩(程序更鲁棒) |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 20260731 课后作业解析:匿名内部类 Comparator 排序
|
|
|
+
|
|
|
+### 作业要求回顾
|
|
|
+
|
|
|
+> 创建 `ArrayList<Student>`,用**匿名内部类**实现 `Comparator<Student>` 分别按年龄升序、成绩降序排序(`Collections.sort`);再创建 `TreeSet<Student>` 传入按姓名排序的匿名 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<Student> 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<Student>() {
|
|
|
+ @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<Student>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Student s1, Student s2) {
|
|
|
+ return Double.compare(s2.getScore(), s1.getScore()); // 成绩降序
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 3. 创建 TreeSet<Student>,传入按姓名排序的匿名 Comparator,迭代器遍历观察自动排序
|
|
|
+ // TreeSet<Student> 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 的构造用比较器排序,存入即自动排序 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 随堂练习要点
|
|
|
+
|
|
|
+### 练习一:自然排序(Comparable)与比较器排序(Comparator)对比
|
|
|
+
|
|
|
+**核心步骤**:
|
|
|
+1. `Student` 类实现 `Comparable<Student>`,`compareTo` 按年龄升序(年龄相同再按姓名)
|
|
|
+2. `new TreeSet<>()` 存入 4 名学生 → 观察自然排序结果
|
|
|
+3. `new TreeSet<>(new Comparator<Student>() {...})` 按成绩升序 → 观察比较器排序结果
|
|
|
+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<Student>(){...})` 按年龄升序
|
|
|
+2. 按成绩降序:`Double.compare(s2.getScore(), s1.getScore())`
|
|
|
+3. `TreeSet<Student>` 传入按姓名排序的匿名 Comparator,迭代器遍历
|
|
|
+
|
|
|
+**思考**:
|
|
|
+1. double 成绩比较为什么不用 `(int)(s1.getScore()-s2.getScore())`?—— double 相减可能有精度误差,强转 int 也不安全;`Double.compare` 是标准做法
|
|
|
+2. 降序为什么交换参数位置即可?—— compare 返回值符号取反,排序方向随之反转
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. 拓展阅读
|
|
|
+
|
|
|
+### 官方文档
|
|
|
+
|
|
|
+- [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.sort() (JDK 17)](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Collections.html#sort(java.util.List,java.util.Comparator))
|
|
|
+- [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 |
|