Просмотр исходного кода

feat: 新增20260803课堂笔记及授课代码分析——比较器排序(Comparable自然排序/Comparator比较器排序)及20260731课后作业解析(Map操作与遍历/自定义异常/匿名内部类排序)

WanJL 3 недель назад
Родитель
Сommit
b73952dce1

+ 2 - 0
README.md

@@ -37,6 +37,7 @@
 | 2026-07-29 | [包装类(Wrapper Classes)+ 集合框架入门(Collection Framework)+ 泛型(Generics):基本类型与包装类对应关系、装箱与拆箱、自动装箱/拆箱、包装类优势、缓存机制、Bean 类应用;集合 vs 数组、体系结构(Collection/List/Set/Map)、线性表数据结构(栈/队列/数组/链表)、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity、trimToSize/ensureCapacity/grow 1.5倍扩容公式、位运算符 >> <<)、ArrayList 常用方法速查;泛型概念与好处、泛型定义格式、通配符 ? / ? extends T、自定义泛型类、有界类型参数、练习参考答案(Student toString/equals/hashCode、泛型 vs 原始类型)](./课堂笔记/01-JavaSE基础-笔记/20260729-笔记.md) |
 | 2026-07-30 | [List.of / LinkedList / 自定义链表 / ArrayList 练习](./课堂笔记/01-JavaSE基础-笔记/20260730-笔记.md) |
 | 2026-07-31 | [LinkedList 多接口形态(List/Queue/Deque)+ Iterator 迭代器 + Map集合(入门/遍历/底层原理)+ 二叉树(二叉搜索树/平衡二叉树/红黑树)](./课堂笔记/01-JavaSE基础-笔记/20260731-笔记.md) |
+| 2026-08-03 | [比较器排序:Comparable 自然排序 / Comparator 比较器排序 + 20260731 课后作业解析(Map 操作与遍历、异常处理综合、匿名内部类 Comparator 排序)](./课堂笔记/01-JavaSE基础-笔记/20260803-笔记.md) |
 
 ## 二、授课代码
 
@@ -60,6 +61,7 @@
 | 2026-07-29 | 包装类 + 集合框架入门 + 泛型(Generics):装箱/拆箱、自动装箱/拆箱、包装类在 Bean 中的应用;集合 vs 数组、体系结构、线性表数据结构、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity、trimToSize/ensureCapacity/grow 1.5倍扩容公式、位移运算符)、ArrayList 常用方法速查;泛型概念与好处、定义格式、通配符、自定义泛型类、有界类型参数、练习参考答案(Student 类重写 toString/equals/hashCode、泛型 vs 原始类型) | `c260729` | [`course/packaging`](./授课代码/c260729/src/course/packaging/) / [`course/collection`](./授课代码/c260729/src/course/collection/) / [`exercises/exercise01`](./授课代码/c260729/src/exericse/exericse01/) / [`exercises/exercise02`](./授课代码/c260729/src/exericse/exericse02/) |
 | 2026-07-30 | List.of() 不可变集合 + LinkedList 双链表理论 + 手写单链表 MyLinked + Student 课程管理练习 (ArrayList 常用操作) + 课后作业回顾手写 MyArrayList | `c260730` | [`course`](./授课代码/c260730/src/course/) / [`exericse/exericse01`](./授课代码/c260730/src/exericse/exericse01/) / [`homework0729/p4`](./授课代码/c260730/src/homework0729/p4/) |
 | 2026-07-31 | LinkedList 多接口形态(List / Queue / Deque)+ Iterator 迭代器遍历(ArrayList/LinkedList 迭代器)+ Map集合:入门(HashMap:put/keySet/values/containsKey)、遍历(keySet+get / entrySet)、HashMap底层原理(数组+链表/红黑树、hashCode/equals、5个关键常量)+ 二叉树(二叉搜索树/平衡二叉树AVL/红黑树规则与旋转)+ 随堂练习参考解答(自定义Key/词频统计) | `c260731` | [`course`](./授课代码/c260731/src/course/) / [`exericse/exericse01`](./授课代码/c260731/src/exericse/exericse01/) / [`exericse/exericse02`](./授课代码/c260731/src/exericse/exericse02/) |
+| 2026-08-03 | 比较器排序:TreeSet/TreeMap 有序集合、Comparable 自然排序(course/Student implements Comparable、compareTo 按年龄二次比较姓名)、Comparator 比较器排序(course/Demo01:TreeSet 构造传入匿名内部类 Comparator);20260731 课后作业解析:Map 集合基本操作与遍历(homework0731/p2_map)、异常处理综合(homework0731/p4_exception:自定义异常 InvalidScoreException + 多 catch 块 + 输入校验)、匿名内部类 Comparator 排序(homework0731/p5_sort:Collections.sort 按年龄升序/成绩降序 Double.compare) | `c260803` | [`course`](./授课代码/c260803/src/course/) / [`homework0731/p2_map`](./授课代码/c260803/src/homework0731/p2_map/) / [`homework0731/p4_exception`](./授课代码/c260803/src/homework0731/p4_exception/) / [`homework0731/p5_sort`](./授课代码/c260803/src/homework0731/p5_sort/) |
 
 ## 三、课堂练习题
 

+ 118 - 0
授课代码/c260803/src/course/Demo01.java

@@ -0,0 +1,118 @@
+package course;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo01
+ * @description 比较器
+ * @create 2026/8/3
+ */
+public class Demo01 {
+    /*
+        我们说过 HashMap以及HashSet都是无序集合,取出的顺序和存入的顺序不是一致。
+        TreeMap和TreeSet都是有序集合,取出的顺序和存入的数组是一致的(注意,其实也不一定是一致的,而是排序过的)。
+        注意:不是所有的类型变量(包括自定义的)都能存入到TreeSet或TreeMap,是有条件的。
+        只有实现了Comparable接口并且重写了compareTo(T o)方法的对象才可以存入。
+        或者 在创建集合对象的时候,在构造方法中传入比较方式--就是使用比较器对象Comparator来进行临时的自定义排序
+
+        我们一般还是以实现Comparable接口为主,只有类定义期间没有实现Comparable接口,或者在加入集合中需要临时做其他方式的排序
+        才会使用Comparator进行临时的自定义排序。
+
+        想一想,现在一个类的完整结构:
+
+        public class 类名 implements Comparable{
+            private 类型 属性1;
+            private 类型 属性2;
+            ....
+            private 类型 属性n;
+
+            无参构造方法
+
+            有参构造方法(参数...){
+            }
+
+            (可选):
+            内部类..
+            代码块...
+
+
+
+            getter方法...
+            setter方法...
+
+            equals()方法....
+            hashCode()方法....
+            toString()方法....
+            compareTo(T o)方法...
+        }
+
+        自然排序:实体类实现Comparable接口,重写compareTo(T o),根据返回值进行排序
+        比较器排序:创建集合对象的时候,传入Comparator接口的实现类对象,重新compare()方法,根据返回值进行排序
+        在使用的时候,默认使用的是自然排序Comparable接口,当自然排序不能满足我们的需求的时候,就需要使用比较器排序。
+
+        两种方式关于返回值的规则:
+            如果返回值为负数,表示当前存入的元素是比较小的值,存左边。
+            如果返回值为0,表示当前存入的元素是相同的,和集合重复了。不存。
+            如果返回值为整数,表示当前存入的元素是比较大的值,存右边。
+
+       所以为什么官方强烈推荐 如果 x1.compareTo(x2) == 0 或者 compare(x1,x2)==0 那么建议 x1.equals(x2)返回true?
+       因为是为了能更好的存入的TreeMap或TreeSet集合。
+       如果我们设置的是根据年龄排序,或者根据某个其他属性进行排序,那么当把它们存入到TreeMap或TreeSet集合中的时候,
+       会出现因为某个属性的相同导致对象存入不进集合。
+       所以强烈建议如果 x1.compareTo(x2) == 0 或者 compare(x1,x2)==0 那么建议 x1.equals(x2)返回true并且hashCode一致。
+
+
+
+
+
+     */
+    public static void main(String[] args) {
+
+//        TreeSet<String> treeSet=new TreeSet<>();
+//        treeSet.add("张三");
+//        treeSet.add("李四");
+//        treeSet.add("王五");
+//        treeSet.add("赵六");
+
+
+        //按照自定义的要求临时进行排序
+        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);
+        }
+
+
+
+        TreeSet<Student> treeSet=new TreeSet<>();
+        treeSet.add(new Student("张三", 20, 85));
+        treeSet.add(new Student("李四", 22, 90));
+        treeSet.add(new Student("王五", 20, 78));
+        treeSet.add(new Student("赵六", 21, 88));
+
+
+
+
+
+        for (Student s: treeSet){
+            System.out.println(s);
+        }
+    }
+
+}

+ 64 - 0
授课代码/c260803/src/course/Student.java

@@ -0,0 +1,64 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description
+ * @create 2026/8/3
+ */
+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;
+    }
+
+    // TODO: 补全 getter / setter 方法
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public double getScore() {
+        return score;
+    }
+
+    public void setScore(double score) {
+        this.score = score;
+    }
+
+    @Override
+    public String toString() {
+        return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
+    }
+
+    /**
+     * 比较器方法
+     * @param o the object to be compared.
+     * @return
+     */
+    @Override
+    public int compareTo(Student o) {
+        //设置按照年龄排序
+        //从小到大排序
+        int result = this.age - o.getAge();
+        return result==0?this.name.compareTo(o.getName()):result;
+    }
+}

+ 61 - 0
授课代码/c260803/src/homework0731/p2_map/MapDemo.java

@@ -0,0 +1,61 @@
+package homework0731.p2_map;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title MapDemo
+ * @description
+ * @create 2026/8/3
+ */
+public class MapDemo {
+    public static void main(String[] args) {
+        // 1. 创建 HashMap<String, Integer>,添加 4 组成绩:
+        //    "x001"->90, "x002"->85, "x003"->92, "x004"->78
+
+        HashMap<String, Integer> hashMap=new HashMap<>();
+        hashMap.put("x001",90);
+        hashMap.put("x002",85);
+        hashMap.put("x003",92);
+        hashMap.put("x004",78);
+        // 2. 输出集合大小(size()):"学生人数:4"
+        int size = hashMap.size();
+        // 3. 查询:get("x002"):"学号 x002 的成绩:85"
+
+        // 4. 判断是否包含某键(containsKey("x001")):true
+        //    判断是否包含不存在的键(containsKey("x999")):false
+
+        // 5. 修改:put("x004", 88)(键已存在 → 覆盖旧值),再次 get 验证
+
+        // 6. 删除:remove("x001"),输出删除后的集合大小
+
+        // 7. 遍历方式一:keySet() + foreach + get(key),输出所有"学号-成绩"
+        //    输出格式:"学号:x002,成绩:85"
+
+        // 8. 遍历方式二:entrySet() + foreach + getKey()/getValue(),输出所有"学号-成绩"
+        Set<Map.Entry<String, Integer>> entries = hashMap.entrySet();
+        for (Map.Entry<String, Integer> entry:entries){
+            System.out.println(entry.getKey()+"--->"+entry.getValue());
+
+        }
+        // 9. 创建 TreeMap<String, Integer>,放入同样的 4 组数据
+        //    用 keySet() 遍历输出,观察:TreeMap 的遍历顺序与 HashMap 有什么不同?
+        //    —— 提示:TreeMap 按键(学号)自动排序
+        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);
+        }
+
+
+    }
+}

+ 14 - 0
授课代码/c260803/src/homework0731/p4_exception/InvalidScoreException.java

@@ -0,0 +1,14 @@
+package homework0731.p4_exception;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title InvalidScoreException
+ * @description
+ * @create 2026/8/3
+ */
+public class InvalidScoreException extends RuntimeException {
+    public InvalidScoreException(String message) {
+        super(message);
+    }
+}

+ 55 - 0
授课代码/c260803/src/homework0731/p4_exception/ScoreStatisticsDemo.java

@@ -0,0 +1,55 @@
+package homework0731.p4_exception;
+
+import java.util.ArrayList;
+import java.util.InputMismatchException;
+import java.util.Iterator;
+import java.util.Scanner;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title ScoreStatisticsDemo
+ * @description
+ * @create 2026/8/3
+ */
+public class ScoreStatisticsDemo {
+    public static void main(String[] args) {
+        Scanner sc = new Scanner(System.in);
+        ArrayList<Integer> scores = new ArrayList<>();
+
+        // 1. 循环输入 5 个成绩(0~100),存入 scores
+        //    提示:for (int i = 0; i < 5; i++) { int s = sc.nextInt(); scores.add(s); }
+        int s =0;
+        for (int i = 0; i < 5;) {
+           try {
+                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之间");
+           }
+
+
+        }
+        // 2. 使用 try-catch 处理以下异常:
+        //    a. 输入不是整数 → InputMismatchException,提示"请输入整数"并要求重新输入
+        //    b. 成绩不在 0~100 → 抛出 InvalidScoreException(自定义异常)
+        //       if (s < 0 || s > 100) { throw new InvalidScoreException("成绩必须在0~100之间"); }
+        //    c. 下标越界 → IndexOutOfBoundsException
+
+        // 3. 计算总分和平均分(用 for 或迭代器遍历求和)
+
+        // 4. 使用迭代器遍历输出所有成绩
+        Iterator<Integer> iterator = scores.iterator();
+        while (iterator.hasNext()){
+            Integer i = iterator.next();
+            System.out.print(i+", ");
+        }
+    }
+}

+ 50 - 0
授课代码/c260803/src/homework0731/p5_sort/SortDemo.java

@@ -0,0 +1,50 @@
+package homework0731.p5_sort;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title SortDemo
+ * @description
+ * @create 2026/8/3
+ */
+public class SortDemo {
+    public static void main(String[] args) {
+        // 1. 创建 ArrayList<Student>,添加 4 名学生:
+        //    张三 20 85 / 李四 22 90 / 王五 19 78 / 赵六 21 88
+        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));
+        // 2. 使用【匿名内部类】实现 Comparator<Student>,按年龄升序排序
+            Collections.sort(list, new Comparator<Student>() {
+                @Override
+                public int compare(Student s1, Student s2) {
+                    return s1.getAge() - s2.getAge();   // 年龄升序
+                }
+            });
+
+        // 3. 输出按年龄排序后的结果
+        for (Student s: list){
+            System.out.println(s);
+        }
+
+
+        // 4. 再写一个匿名内部类 Comparator,按成绩(score)降序排序,输出结果
+        //    提示:成绩是 double,比较可用 Double.compare(s2.getScore(), s1.getScore())
+
+        Collections.sort(list, new Comparator<Student>() {
+            @Override
+            public int compare(Student s1, Student s2) {
+                return Double.compare(s2.getScore(), s1.getScore());   // 年龄降序
+            }
+        });
+
+        // 5. 创建一个 TreeSet<Student>,传入按姓名(name)排序的匿名 Comparator
+        //    把 4 名学生放入 TreeSet,用迭代器遍历输出,观察自动排序效果
+    }
+}

+ 51 - 0
授课代码/c260803/src/homework0731/p5_sort/Student.java

@@ -0,0 +1,51 @@
+package homework0731.p5_sort;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description
+ * @create 2026/8/3
+ */
+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;
+    }
+
+    // TODO: 补全 getter / setter 方法
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public double getScore() {
+        return score;
+    }
+
+    public void setScore(double score) {
+        this.score = score;
+    }
+
+    @Override
+    public String toString() {
+        return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
+    }
+}

+ 495 - 0
课堂笔记/01-JavaSE基础-笔记/20260803-笔记.md

@@ -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 |