|
|
@@ -0,0 +1,523 @@
|
|
|
+# 2026-08-03 比较器排序专项练习(课堂练习用)
|
|
|
+
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**比较器排序(Comparable 自然排序 / Comparator 比较器排序)** 展开,涵盖 Comparable 接口实现与 `compareTo(T o)` 重写、Comparator 接口与匿名内部类、TreeSet/TreeMap 有序集合自动排序、返回值规则(负数存左 / 0 重复不存 / 正数存右)、`Collections.sort(list, comparator)`、`Double.compare`、compareTo 与 equals 一致性等知识点。
|
|
|
+>
|
|
|
+> 题目分为 **基础练习**(Comparable 自然排序 / Comparator 比较器排序 / Collections.sort 排序,3 题)和 **进阶挑战**(比较器综合应用,2 题)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第一部分:基础练习
|
|
|
+
|
|
|
+> 以下练习围绕今日授课核心内容展开,由浅入深。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 1:Comparable 自然排序(TreeSet 自动排序 + 去重)
|
|
|
+
|
|
|
+**难度**:⭐
|
|
|
+**知识点**:`Comparable<T>` 接口、`compareTo(T o)` 重写、`TreeSet` 有序 + 去重、返回值规则
|
|
|
+
|
|
|
+**场景描述**:TreeSet 是有序集合,取出的顺序是**排序过**的。不是所有类型都能直接存入 TreeSet——必须实现 `Comparable` 接口(自然排序),或在创建集合时于构造方法中传入 `Comparator`(比较器排序)。本节课 Student 类实现自然排序:**按年龄升序,年龄相同再按姓名排序**。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+1. 定义 `Student` 类(`name`、`age`、`score` 三个属性),**实现 `Comparable<Student>` 接口**,重写 `compareTo(T o)` 方法:
|
|
|
+
|
|
|
+```java
|
|
|
+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 / toString 方法
|
|
|
+
|
|
|
+ // TODO: 重写 compareTo —— 按年龄升序,年龄相同再按姓名
|
|
|
+ @Override
|
|
|
+ public int compareTo(Student o) {
|
|
|
+ // 提示:int result = this.age - o.getAge();
|
|
|
+ // return result == 0 ? this.name.compareTo(o.getName()) : result;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+2. 编写测试类 `NaturalSortTest`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.TreeSet;
|
|
|
+
|
|
|
+public class NaturalSortTest {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 TreeSet<Student>(不传 Comparator → 使用自然排序)
|
|
|
+ // 2. 添加 4 名学生:张三20 / 李四22 / 王五20 / 赵六21(注意:张三和王五年龄相同)
|
|
|
+ // 3. foreach 遍历输出,观察自动排序结果
|
|
|
+ // 4. 再添加一名"刘七20",观察:刘七能否存入?为什么?(提示:compareTo 返回值规则)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+张三 20 85
|
|
|
+王五 20 78 ← 年龄 20 与"张三"相同 → 触发姓名二次比较(王 < 张)
|
|
|
+赵六 21 88
|
|
|
+李四 22 90
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. 返回值为负数 / 0 / 正数分别代表什么?元素会存入左边、视为重复不存、还是存入右边?
|
|
|
+2. 为什么年龄相同的"张三"和"王五"都能存入?—— compareTo 里做了**二次比较**(姓名),返回值不为 0,所以不视为重复
|
|
|
+3. 如果 compareTo 只按年龄比较,添加"刘七20"会发生什么?(提示:20 与已有节点比较返回 0 → 视为重复不存)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 2:Comparator 比较器排序(TreeSet 构造传入匿名内部类)
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:`Comparator<T>` 接口、`compare(o1, o2)` 重写、匿名内部类、`TreeSet(Comparator)` 构造方法
|
|
|
+
|
|
|
+**场景描述**:类设计期间已经实现 Comparable(默认按年龄排序),但现在**临时**需要按别的规则排序(如成绩),又**不修改** Student 类。此时使用**比较器排序**——创建集合对象时,在构造方法中传入 `Comparator` 接口的实现类对象(匿名内部类)。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.TreeSet;
|
|
|
+
|
|
|
+public class ComparatorSortTest {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 TreeSet<Student>,构造方法传入匿名内部类 Comparator<Student>
|
|
|
+ // 排序规则:按成绩(score)升序,成绩相同再按姓名排序
|
|
|
+ // 提示: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;
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+
|
|
|
+ // 2. 添加 4 名学生:张三85 / 李四90 / 王五78 / 赵六88
|
|
|
+ // 3. foreach 遍历输出,观察:这次是按【成绩】排序,而不是年龄
|
|
|
+ // 4. 与练习 1 的输出顺序对比,体会"自然排序 vs 比较器排序"
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+王五 20 78
|
|
|
+张三 7 85
|
|
|
+赵六 222 88
|
|
|
+李四 2 90
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. 为什么 `new TreeSet<>(new Comparator<Student>(){...})` 就能覆盖默认的自然排序?
|
|
|
+2. Comparator 的 `compare(o1, o2)` 和 Comparable 的 `compareTo(o)` 写法有什么不同?(提示:一个是"两个对象在外部比较",一个是"this 与 o 比较")
|
|
|
+3. 匿名内部类为什么适合用来传 Comparator?(提示:临时规则、用完即弃、无需新建类文件)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 3:Collections.sort 对 List 排序(按年龄升序 / 按成绩降序)
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:`Collections.sort(list, comparator)`、匿名内部类、`Double.compare(a, b)`、降序技巧(交换参数)
|
|
|
+
|
|
|
+**场景描述**:`ArrayList` 是**无序**的,想让它按指定规则排序,用 `Collections.sort(list, comparator)`——传入匿名内部类 Comparator,对 List **原地排序**(改变原集合顺序)。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Comparator;
|
|
|
+
|
|
|
+public class ListSortTest {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 ArrayList<Student>,添加 4 名学生:张三20-85 / 李四22-90 / 王五19-78 / 赵六21-88
|
|
|
+ // 2. 匿名内部类 Comparator:按年龄升序排序(s1.getAge() - s2.getAge())
|
|
|
+ // Collections.sort(list, new Comparator<Student>() {...});
|
|
|
+ // 遍历输出
|
|
|
+
|
|
|
+ // 3. 匿名内部类 Comparator:按成绩(double)降序排序
|
|
|
+ // 注意:成绩是 double,不能直接相减强转 int
|
|
|
+ // 提示:Double.compare(s2.getScore(), s1.getScore()) ← 注意参数顺序 = 降序
|
|
|
+ // 遍历输出
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+========== 按年龄升序 ==========
|
|
|
+王五 19 78
|
|
|
+张三 20 85
|
|
|
+赵六 21 88
|
|
|
+李四 22 90
|
|
|
+========== 按成绩降序 ==========
|
|
|
+李四 22 90
|
|
|
+赵六 21 88
|
|
|
+张三 20 85
|
|
|
+王五 19 78
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. double 类型的成绩为什么不能用 `(int)(s1.getScore() - s2.getScore())`?用 `Double.compare` 有什么好处?(提示:浮点精度 + 强转截断)
|
|
|
+2. `Collections.sort(list, comparator)` 和 `TreeSet` 自动排序的区别是什么?(提示:一个改变已有 List 的顺序,一个在存入时排序;TreeSet 还会去重)
|
|
|
+3. 想让 Comparator 排序方向反过来,最简单的做法是什么?(提示:交换 compare 中两个参数的位置)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第二部分:进阶挑战
|
|
|
+
|
|
|
+> 以下练习综合运用 Comparable / Comparator、TreeMap、compareTo 与 equals 一致性等知识。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 进阶 1:成绩排行榜(TreeMap + Comparable 键排序)
|
|
|
+
|
|
|
+**难度**:⭐⭐⭐
|
|
|
+**知识点**:`TreeMap<K,V>` 按键排序、自定义类型作为 Key、`entrySet()` 遍历、compareTo/equals 一致性
|
|
|
+
|
|
|
+**场景描述**:TreeMap 是**按键排序**的有序 Map。把 `Student` 对象作为 **Key**、班级作为 Value 存入 TreeMap。因为 Student 实现了 Comparable,TreeMap 会自动按 compareTo 的规则对键排序——实现"成绩排行榜"。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+1. 定义 `Student` 类(实现 Comparable,**按成绩降序**排序,成绩相同按姓名),修改 compareTo:
|
|
|
+
|
|
|
+```java
|
|
|
+public class Student implements Comparable<Student> {
|
|
|
+ private String name;
|
|
|
+ private int age;
|
|
|
+ private double score;
|
|
|
+
|
|
|
+ // 构造方法、getter/setter、toString(略)
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int compareTo(Student o) {
|
|
|
+ // TODO: 按成绩降序(注意:谁减谁决定升降序)
|
|
|
+ // 提示:int result = Double.compare(o.getScore(), this.score); // 降序
|
|
|
+ // return result == 0 ? this.name.compareTo(o.getName()) : result;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+2. 编写测试类 `ScoreRankingTest`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.Map;
|
|
|
+import java.util.TreeMap;
|
|
|
+
|
|
|
+public class ScoreRankingTest {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 TreeMap<Student, String>:学生 → 所在班级
|
|
|
+ // 提示:Map<Student, String> map = new TreeMap<>();
|
|
|
+ // 添加:张三90-"一班"、李四85-"二班"、王五95-"三班"、赵六88-"一班"
|
|
|
+ // 2. entrySet() 遍历输出,观察是否按成绩从高到低排序
|
|
|
+ // 输出格式:"王五 95 - 三班"
|
|
|
+ // 3. 思考:如果两个学生成绩相同且姓名相同(属性完全一致),再 put 会发生什么?
|
|
|
+ // —— compareTo 返回 0 → 视为同一个键 → 覆盖旧值(去重)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+王五 95 - 三班
|
|
|
+张三 90 - 一班
|
|
|
+赵六 88 - 一班
|
|
|
+李四 85 - 二班
|
|
|
+```
|
|
|
+
|
|
|
+**原理分析(必答)**:
|
|
|
+1. TreeMap 是怎么判断两个键是否重复的?—— 调用键的 `compareTo` 方法,返回 0 即视为重复
|
|
|
+2. 官方为什么强烈推荐"compareTo == 0 时 equals 返回 true、hashCode 一致"?—— 保证排序判定与相等判定保持一致,避免集合去重行为出现矛盾
|
|
|
+3. 如果 Student 没有实现 Comparable 也不传 Comparator,`new TreeMap<Student, String>()` 会怎样?(提示:存入第一个键时抛 ClassCastException)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 进阶 2:员工信息管理系统(多排序规则 + compareTo/equals 一致性 + 综合应用)
|
|
|
+
|
|
|
+**难度**:⭐⭐⭐⭐
|
|
|
+**知识点**:Comparable 与 Comparator 结合、`hashCode()` / `equals()` 重写、compareTo 与 equals 一致性、多维度排序、迭代器遍历
|
|
|
+
|
|
|
+**场景描述**:某公司开发员工信息管理系统。员工类 `Employee`(工号、姓名、薪资、部门)。要求:
|
|
|
+- **默认排序**(自然排序):按薪资降序,薪资相同再按工号
|
|
|
+- 员工加入 TreeSet 后**自动排序**且**自动去重**(工号相同的员工视为同一人)
|
|
|
+- 临时需求:按姓名排序输出一份通讯录(使用 Comparator 比较器排序,不修改 Employee 类)
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+1. 定义 `Employee` 类:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+public class Employee implements Comparable<Employee> {
|
|
|
+ private String id; // 工号
|
|
|
+ private String name; // 姓名
|
|
|
+ private double salary; // 薪资
|
|
|
+ private String dept; // 部门
|
|
|
+
|
|
|
+ public Employee(String id, String name, double salary, String dept) {
|
|
|
+ this.id = id;
|
|
|
+ this.name = name;
|
|
|
+ this.salary = salary;
|
|
|
+ this.dept = dept;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: 补全 getter / setter / toString 方法
|
|
|
+
|
|
|
+ // TODO: 重写 compareTo —— 按薪资降序,薪资相同再按工号
|
|
|
+ @Override
|
|
|
+ public int compareTo(Employee o) {
|
|
|
+ // 提示:Double.compare(o.getSalary(), this.salary) // 降序
|
|
|
+ // 若返回 0,再比较工号:this.id.compareTo(o.getId())
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: 重写 equals / hashCode —— 与 compareTo 保持一致(工号相同即视为同一人)
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object o) { ... }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() { ... }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+2. 编写测试类 `EmployeeSystem`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.TreeSet;
|
|
|
+
|
|
|
+public class EmployeeSystem {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 TreeSet<Employee>(自然排序)
|
|
|
+ // 添加 5 名员工(其中两名工号相同,测试去重):
|
|
|
+ // E001-张三-12000-技术部 / E002-李四-15000-市场部
|
|
|
+ // E003-王五-15000-技术部 / E004-赵六-9000-财务部
|
|
|
+ // E005-刘七-12000-市场部
|
|
|
+
|
|
|
+ // 2. 迭代器遍历输出 → 观察:按薪资降序,薪资相同按工号;工号重复的自动去重
|
|
|
+ // 输出格式:"E001-张三-12000.0-技术部"
|
|
|
+
|
|
|
+ // 3. 临时需求:通讯录按姓名排序 —— 创建 TreeSet<Employee> 传入按姓名排序的 Comparator
|
|
|
+ // 匿名内部类:o1.getName().compareTo(o2.getName())
|
|
|
+ // 迭代器遍历输出
|
|
|
+
|
|
|
+ // 4. 思考:E001-张三(12000) 和 E005-刘七(12000) 薪资相同,为何都能存入?
|
|
|
+ // —— compareTo 在薪资相同后继续比较工号,返回值不为 0
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+========== 默认排序:按薪资降序(薪资相同按工号) ==========
|
|
|
+E002-李四-15000.0-市场部
|
|
|
+E003-王五-15000.0-技术部 ← 与李四薪资相同 → 按工号 E002<E003
|
|
|
+E001-张三-12000.0-技术部
|
|
|
+E005-刘七-12000.0-市场部 ← 与张三薪资相同 → 按工号 E001<E005
|
|
|
+E004-赵六-9000.0-财务部
|
|
|
+========== 通讯录:按姓名排序 ==========
|
|
|
+E002-李四-15000.0-市场部
|
|
|
+E005-刘七-12000.0-市场部
|
|
|
+E003-王五-15000.0-技术部
|
|
|
+E001-张三-12000.0-技术部
|
|
|
+E004-赵六-9000.0-财务部
|
|
|
+```
|
|
|
+
|
|
|
+**原理分析(必答)**:
|
|
|
+1. 为什么 `E002-李四` 和 `E003-王五` 薪资相同(都是 15000)却能都存入 TreeSet?—— compareTo 返回 0 才算重复,薪资相同后还比较了工号,返回值不为 0
|
|
|
+2. 官方推荐"compareTo == 0 → equals 返回 true、hashCode 一致",Employee 中应该按什么来重写 equals/hashCode?—— 按与 compareTo 一致的判定依据(本例为工号)
|
|
|
+3. 为什么两次遍历用迭代器而不是 for + get(i)?—— Set 没有索引,Iterator 是单列集合的通用遍历方式
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 参考答案要点
|
|
|
+
|
|
|
+## 练习 1 参考
|
|
|
+
|
|
|
+```java
|
|
|
+public class Student implements Comparable<Student> {
|
|
|
+ private String name;
|
|
|
+ private int age;
|
|
|
+ private double 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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// NaturalSortTest
|
|
|
+TreeSet<Student> set = new TreeSet<>();
|
|
|
+set.add(new Student("张三", 20, 85));
|
|
|
+set.add(new Student("李四", 22, 90));
|
|
|
+set.add(new Student("王五", 20, 78));
|
|
|
+set.add(new Student("赵六", 21, 88));
|
|
|
+
|
|
|
+for (Student s : set) {
|
|
|
+ System.out.println(s);
|
|
|
+}
|
|
|
+// 输出顺序:王五20 → 张三20 → 赵六21 → 李四22(年龄升序,20 岁两人按姓名排序)
|
|
|
+```
|
|
|
+
|
|
|
+## 练习 2 参考
|
|
|
+
|
|
|
+```java
|
|
|
+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);
|
|
|
+}
|
|
|
+// 输出顺序:王五78 → 张三85 → 赵六88 → 李四90(成绩升序,与年龄无关)
|
|
|
+```
|
|
|
+
|
|
|
+## 练习 3 参考
|
|
|
+
|
|
|
+```java
|
|
|
+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));
|
|
|
+
|
|
|
+// 按年龄升序
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+// 按成绩降序(Double.compare + 交换参数 = 降序)
|
|
|
+Collections.sort(list, new Comparator<Student>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Student s1, Student s2) {
|
|
|
+ return Double.compare(s2.getScore(), s1.getScore());
|
|
|
+ }
|
|
|
+});
|
|
|
+for (Student s : list) {
|
|
|
+ System.out.println(s);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 进阶 1 参考
|
|
|
+
|
|
|
+```java
|
|
|
+// Student.compareTo:按成绩降序,成绩相同按姓名
|
|
|
+@Override
|
|
|
+public int compareTo(Student o) {
|
|
|
+ int result = Double.compare(o.getScore(), this.score); // 降序
|
|
|
+ return result == 0 ? this.name.compareTo(o.getName()) : result;
|
|
|
+}
|
|
|
+
|
|
|
+// ScoreRankingTest
|
|
|
+TreeMap<Student, String> map = new TreeMap<>();
|
|
|
+map.put(new Student("张三", 18, 90), "一班");
|
|
|
+map.put(new Student("李四", 19, 85), "二班");
|
|
|
+map.put(new Student("王五", 20, 95), "三班");
|
|
|
+map.put(new Student("赵六", 18, 88), "一班");
|
|
|
+
|
|
|
+for (Map.Entry<Student, String> me : map.entrySet()) {
|
|
|
+ System.out.println(me.getKey().getName() + " " + me.getKey().getScore() + " - " + me.getValue());
|
|
|
+}
|
|
|
+// 输出顺序:王五95 → 张三90 → 赵六88 → 李四85(成绩降序)
|
|
|
+```
|
|
|
+
|
|
|
+## 进阶 2 参考(核心逻辑)
|
|
|
+
|
|
|
+```java
|
|
|
+// Employee.compareTo:按薪资降序,薪资相同按工号
|
|
|
+@Override
|
|
|
+public int compareTo(Employee o) {
|
|
|
+ int result = Double.compare(o.getSalary(), this.salary); // 薪资降序
|
|
|
+ return result == 0 ? this.id.compareTo(o.getId()) : result;
|
|
|
+}
|
|
|
+
|
|
|
+// equals / hashCode:与 compareTo 一致,以工号判定
|
|
|
+@Override
|
|
|
+public boolean equals(Object o) {
|
|
|
+ if (this == o) return true;
|
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
|
+ Employee employee = (Employee) o;
|
|
|
+ return Objects.equals(id, employee.id);
|
|
|
+}
|
|
|
+
|
|
|
+@Override
|
|
|
+public int hashCode() {
|
|
|
+ return Objects.hash(id);
|
|
|
+}
|
|
|
+
|
|
|
+// EmployeeSystem
|
|
|
+TreeSet<Employee> set = new TreeSet<>();
|
|
|
+set.add(new Employee("E001", "张三", 12000, "技术部"));
|
|
|
+set.add(new Employee("E002", "李四", 15000, "市场部"));
|
|
|
+set.add(new Employee("E003", "王五", 15000, "技术部"));
|
|
|
+set.add(new Employee("E004", "赵六", 9000, "财务部"));
|
|
|
+set.add(new Employee("E005", "刘七", 12000, "市场部"));
|
|
|
+
|
|
|
+Iterator<Employee> it = set.iterator();
|
|
|
+while (it.hasNext()) {
|
|
|
+ System.out.println(it.next());
|
|
|
+}
|
|
|
+// 输出:E002 → E003 → E001 → E005 → E004(薪资降序,同薪按工号)
|
|
|
+
|
|
|
+// 通讯录:按姓名排序的临时 Comparator
|
|
|
+TreeSet<Employee> byName = new TreeSet<>(new Comparator<Employee>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Employee o1, Employee o2) {
|
|
|
+ return o1.getName().compareTo(o2.getName());
|
|
|
+ }
|
|
|
+});
|
|
|
+byName.addAll(set);
|
|
|
+for (Employee e : byName) {
|
|
|
+ System.out.println(e);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 涵盖知识点总览
|
|
|
+
|
|
|
+| 知识点 | 对应练习 | 说明 |
|
|
|
+|--------|---------|------|
|
|
|
+| Comparable 接口与 compareTo 重写 | 练习 1、进阶 1、进阶 2 | 实体类内建默认排序规则(自然排序) |
|
|
|
+| 返回值规则(负数/0/正数) | 练习 1、练习 2 | 负数存左、0 重复不存、正数存右 |
|
|
|
+| 二次比较(多字段排序) | 练习 1、进阶 1、进阶 2 | 主排序相同 → 叠加次排序字段,避免误判重复 |
|
|
|
+| Comparator 接口与匿名内部类 | 练习 2、练习 3 | 临时自定义排序,不改实体类 |
|
|
|
+| TreeSet(Comparator) 构造方法 | 练习 2 | 构造方法传入比较器 → 覆盖自然排序 |
|
|
|
+| Collections.sort(list, comparator) | 练习 3 | 对 List 原地排序 |
|
|
|
+| Double.compare(a, b) | 练习 3、进阶 1、进阶 2 | double 比较的标准做法,避免精度问题 |
|
|
|
+| 降序技巧(交换参数) | 练习 3、进阶 2 | compare(s2, s1) / compareTo 中交换比较顺序 |
|
|
|
+| TreeMap 按键排序 | 进阶 1 | 自定义类型作为 Key,按 compareTo 排序并去重 |
|
|
|
+| compareTo 与 equals/hashCode 一致性 | 进阶 1、进阶 2 | compareTo == 0 → equals 返回 true、hashCode 一致 |
|
|
|
+| TreeSet 自动排序 + 去重 | 练习 1、进阶 2 | compareTo 返回 0 视为重复,不存入 |
|
|
|
+| Iterator 迭代器遍历 | 进阶 2 | Set 没有索引,用迭代器统一遍历 |
|
|
|
+
|