2026年8月3日-作业.md 19 KB

2026年8月3日 课后作业 — 比较器排序 / Collections 工具类 / 可变参数 / Lambda 表达式

说明:本次作业围绕今日授课核心内容——比较器排序(Comparable 自然排序 / Comparator 比较器排序)、集合工具类 Collections、可变参数 Varargs 与 Lambda 表达式(函数式接口) 展开,涵盖 compareTo/compare 返回值规则、二次比较(多字段排序)、Collections.sort + 匿名内部类、Double.compare 降序技巧、可变参数本质与置末规则、Lambda 标准格式与三条省略规则、compareTo 与 equals/hashCode 一致性等知识点,并附 1 道简答题(Lambda vs 匿名内部类)。

题目分为 ⭐ 基础(必做):作业 1~3;⭐⭐ 进阶(必做):作业 4~5;简答题(必做)。请新建 Java 项目,编写并运行代码,将运行结果截图提交。


📌 本次作业重要说明

命名要求

项目 要求
包名 必须按题目指定的包名创建(全小写)
类名 自由命名,但需见名知意
测试类 建议命名为 TestXxx 或 XxxTest
文件名 必须与类名一致

提交目录结构示例

提交\
└─ 张三\
    ├─ p1_comparable\          ← 第 1 题的包(Comparable 自然排序)
    │   ├─ Student.java
    │   └─ ScoreRankingTest.java
    ├─ p2_comparator\          ← 第 2 题的包(Comparator + Collections 工具类)
    │   ├─ Product.java
    │   └─ ProductSortDemo.java
    ├─ p3_varargs\             ← 第 3 题的包(可变参数)
    │   ├─ Calculator.java
    │   └─ ScoreCalculator.java
    ├─ p4_lambda\              ← 第 4 题的包(Lambda 表达式)
    │   ├─ Addable.java
    │   ├─ Flyable.java
    │   └─ LambdaDemo.java
    └─ p5_employee\            ← 第 5 题的包(综合应用)
        ├─ Employee.java
        └─ EmployeeSystem.java

⚠ 注意:

  1. 包名必须严格按题目要求书写(全小写),目录结构必须与包结构一致
  2. 每个 Java 文件头部需包含 @author、@title、@create 文档注释
  3. 简答题(Lambda 与匿名内部类)请写在 Word / TXT 文档中,与代码一起提交

第一部分:基础题(⭐ 必做)


作业 1:Comparable 自然排序 — 学生成绩排行榜

包名:p1_comparable

知识点:Comparable<T> 接口、compareTo(T o) 重写、返回值规则(负数存左 / 0 重复不存 / 正数存右)、二次比较、TreeSet 自动排序 + 去重

场景描述:编写学生成绩排行榜。Student 类实现 Comparable(按成绩降序,成绩相同再按姓名),存入 TreeSet 后自动排序并自动去重。

题目要求:

  1. 定义 Student 类(name、age、score),实现 Comparable<Student>,重写 compareTo:

    // Student.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 = Double.compare(o.getScore(), this.score);  // 降序
        //       return result == 0 ? this.name.compareTo(o.getName()) : result;
    }
    }
    
  2. 编写测试类 ScoreRankingTest:

    import java.util.TreeSet;
    
    public class ScoreRankingTest {
    public static void main(String[] args) {
        // 1. 创建 TreeSet<Student>(不传 Comparator → 使用自然排序)
        // 2. 添加 5 名学生:
        //    张三 18 90 / 李四 19 85 / 王五 20 95 / 赵六 18 90 / 孙七 21 88
        //    注意:张三和赵六【成绩相同】→ 应触发姓名二次比较,两人都能存入
        // 3. foreach 遍历输出,观察:成绩降序、同分按姓名排序
        // 4. 再添加一名"刘八 22 90"(与张三成绩相同),观察去重行为
    }
    }
    

预期输出:

========== 学生成绩排行榜 ==========
王五 20 95.0
张三 18 90.0
赵六 18 90.0      ← 与张三同分 → 按姓名二次比较(张 < 赵),都能存入
孙七 21 88.0
李四 19 85.0

思考题(选做,不要求提交代码):

  1. 返回值负数 / 0 / 正数分别代表什么?存入 TreeSet 时如何处理?
  2. 为什么张三和赵六成绩相同(90)却都能存入?—— compareTo 做了二次比较(姓名),返回值不为 0
  3. 如果 compareTo 只按成绩比较,会出现什么现象?(提示:同分视为重复,后一个不存)

作业 2:Comparator 比较器排序 + Collections 工具类 — 商品价格排序

包名:p2_comparator

知识点:Collections.sort(list, comparator)、匿名内部类实现 Comparator、Double.compare、降序技巧(交换参数)、Collections.max/min/binarySearch

场景描述:Product 类(名称 + 价格 + 库存)不实现 Comparable。用 Collections.sort + 匿名内部类 Comparator 对商品进行多种临时排序,并练习 Collections 工具类的查找方法。

题目要求:

  1. 定义 Product 类(name、price、stock,补全 getter/setter/toString):

    // Product.java
    public class Product {
    private String name;
    private double price;
    private int stock;
    
    public Product(String name, double price, int stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }
    
    // TODO: 补全 getter / setter / toString 方法
    }
    
  2. 编写 ProductSortDemo:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    
    public class ProductSortDemo {
    public static void main(String[] args) {
        // 1. 创建 ArrayList<Product>,添加 5 个商品:
        //    苹果 3.5 100 / 香蕉 2.0 200 / 西瓜 6.8 50 / 葡萄 12.5 80 / 橙子 5.0 150
    
        // 2. 匿名内部类 Comparator:按价格升序排序
        //    提示:Collections.sort(list, new Comparator<Product>() {
        //              public int compare(Product p1, Product p2) {
        //                  return Double.compare(p1.getPrice(), p2.getPrice());
        //              }
        //          });
        //    遍历输出
    
        // 3. 匿名内部类 Comparator:按价格【降序】排序(交换参数位置)
        //    遍历输出
    
        // 4. 匿名内部类 Comparator:按库存升序排序(int 直接相减)
        //    遍历输出
    
        // 5. 用 Collections.max(list, comparator) 找出价格最高的商品
        //    Collections.min(list, comparator) 找出价格最低的商品
    
        // 6. 用 Collections.binarySearch(list, 目标商品, comparator) 查找(先按价格排序再查找)
    }
    }
    

预期输出(节选):

========== 按价格升序 ==========
香蕉 2.0 200
苹果 3.5 100
橙子 5.0 150
西瓜 6.8 50
葡萄 12.5 80
========== 按价格降序 ==========
葡萄 12.5 80
西瓜 6.8 50
橙子 5.0 150
苹果 3.5 100
香蕉 2.0 200
========== 按库存升序 ==========
西瓜 6.8 50
葡萄 12.5 80
苹果 3.5 100
橙子 5.0 150
香蕉 2.0 200
价格最高的商品:葡萄 12.5
价格最低的商品:香蕉 2.0

思考题(选做,不要求提交代码):

  1. double 类型的价格为什么不能用 (int)(p1.getPrice() - p2.getPrice())?(提示:浮点精度 + 强转截断)
  2. 想让 Comparator 排序方向反过来,最简单的做法是什么?(提示:交换 compare 中两个参数的位置)
  3. Collections.binarySearch 使用前提是什么?(提示:查找前必须先按相同规则排序)

作业 3:可变参数 Varargs — 任意个数成绩统计

包名:p3_varargs

知识点:可变参数 int... a、for each 遍历、可变参数本质即数组、固定参数 + 可变参数(可变参数置末)、求总分/平均分/最高分

场景描述:用可变参数实现"任意个数成绩的统计"——一个方法接收 2 个、3 个、5 个...任意个数的成绩,并计算总分、平均分、最高分。

题目要求:

  1. 定义 Calculator 类,实现 add(int... a) 任意个数整数求和:

    // Calculator.java
    public class Calculator {
    // TODO: 可变参数方法 add(int... a),累加求和
    //   提示:int sum = 0;
    //         for (int n : a) { sum += n; }
    //         return sum;
    public int add(int... a) {
        return 0;  // 请补全
    }
    }
    
  2. 定义 ScoreCalculator 类,实现"固定参数 + 可变参数"的成绩统计:

    // ScoreCalculator.java
    public class ScoreCalculator {
    // TODO: showScore(String name, int... scores)
    //   1) 输出学生姓名
    //   2) 遍历 scores 求总分(total)和最高分(max)
    //   3) 输出各科成绩、总分、平均分(total * 1.0 / scores.length)
    
    public static void main(String[] args) {
        Calculator calc = new Calculator();
    
        // 1. 同一个 add 方法,传入不同个数的参数:
        //    add(1, 2) → 3
        //    add(1, 2, 3) → 6
        //    add(1, 2, 3, 4, 5) → 15
    
        // 2. 验证"可变参数本质就是数组":用 int[] 调用 add(arr)
    
        ScoreCalculator sc = new ScoreCalculator();
        // 3. 张三 3 门课:85, 90, 78
        // 4. 李四 5 门课:92, 88, 95, 80, 90
    }
    }
    

预期输出:

add(1, 2) = 3
add(1, 2, 3) = 6
add(1, 2, 3, 4, 5) = 15
用数组调用 add(arr) = 15
========== 张三的成绩单 ==========
姓名:张三
各科成绩:85, 90, 78
总分:253,平均分:84.33,最高分:90
========== 李四的成绩单 ==========
姓名:李四
各科成绩:92, 88, 95, 80, 90
总分:445,平均分:89.0,最高分:95

思考题(选做,不要求提交代码):

  1. int... a 和 int[] a 是什么关系?为什么调用效果一样?(提示:可变参数是数组的语法糖)
  2. showScore(String name, int... scores) 中可变参数能写在 name 前面吗?(提示:可变参数必须放在参数列表最后)
  3. 如果调用 showScore("王五")(不传成绩),scores.length 是多少?会不会报错?

第二部分:进阶题(⭐⭐ 必做)


作业 4:Lambda 表达式 — 简化接口实现与排序

包名:p4_lambda

知识点:函数式接口、Lambda 标准格式 (参数)->{代码块}、三条省略规则(类型可全省 / 单参数省小括号 / 单语句省大括号分号 return)、Collections.sort + Lambda

场景描述:今日课上新学了 Lambda 表达式——它是另一种形式的匿名内部类,能大幅简化代码。要求用 Lambda 实现两个函数式接口,并用 Lambda 改写 Collections.sort 的排序代码,体会省略模式的便捷。

题目要求:

  1. 定义两个函数式接口:

    // Addable.java —— 有参有返回值
    public interface Addable {
    int add(int x, int y);
    }
    
    // Flyable.java —— 有参无返回值
    public interface Flyable {
    void fly(String s);
    }
    
  2. 编写 LambdaDemo:

    public class LambdaDemo {
    public static void useAdd(Addable a) {
        System.out.println(a.add(10, 20));
    }
    
    public static void useFlyable(Flyable f) {
        f.fly("今天天气不错...是风和日丽的...");
    }
    
    public static void main(String[] args) {
        // 1. Addable:完整写法 useAdd((a, b) -> { return a + b; });
        //    省略写法:代码块只有一条语句 → 省略大括号、分号、return
        useAdd((a, b) -> a + b);
    
        // 2. Flyable:参数有且只有一个 → 小括号可以省略
        useFlyable(s -> {
            System.out.println(s);
            System.out.println("长翅膀....飞...");
        });
    
        // 3. 用 Lambda 改写 Collections.sort:定义学生集合,按成绩升序排序
        //    对比匿名内部类写法:
        //    Collections.sort(list, new Comparator<Student>() {...});
        //    Lambda 写法:Collections.sort(list, (s1, s2) -> ...);
    }
    }
    
  3. 进阶挑战:定义一个 Product 类(名称 + 价格),用 Lambda + Collections.sort 按价格降序排序输出(提示:Double.compare(s2.getPrice(), s1.getPrice()))。

预期输出:

30
今天天气不错...是风和日丽的...
长翅膀....飞...
========== Collections.sort 用 Lambda 排序 ==========
(按你的学生/商品数据排序输出)

思考题(选做,不要求提交代码):

  1. Lambda 表达式的三要素是什么?三条省略规则分别是什么?
  2. 为什么 Lambda 只能用在只有一个抽象方法的接口上?(提示:函数式接口,() 才能唯一确定指向它)
  3. Lambda 与匿名内部类的本质区别是什么?(提示:编译期是否产生独立 .class 文件)

作业 5:综合应用 — 员工信息管理系统

包名:p5_employee

知识点:Comparable 自然排序 + equals/hashCode 一致性 + TreeSet 自动排序/去重 + 匿名内部类或 Lambda 按姓名排序 + 迭代器遍历

场景描述:某公司员工信息管理系统。Employee 类(工号、姓名、薪资、部门)要求:

  • 默认排序:按薪资降序,薪资相同再按工号(自然排序)
  • 存入 TreeSet 后自动排序且自动去重(工号相同的员工视为同一人 → equals/hashCode 与 compareTo 判定口径一致)
  • 临时需求:按姓名排序输出一份通讯录(不修改 Employee 类,用 Comparator 匿名内部类或 Lambda)

题目要求:

  1. 定义 Employee 类:

    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) {
        // 提示:int result = Double.compare(o.getSalary(), this.salary);  // 降序
        //       return result == 0 ? this.id.compareTo(o.getId()) : result;
    }
    
    // TODO: 重写 equals / hashCode —— 与 compareTo 判定口径一致(以工号判定)
    //   提示:equals 用 Objects.equals(id, employee.id);hashCode 用 Objects.hash(id)
    }
    
  2. 编写 EmployeeSystem:

    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-市场部 / 再添加一个工号重复的 E002-李四-20000-市场部
    
        // 2. 迭代器遍历输出 → 观察:按薪资降序,薪资相同按工号;工号重复的自动去重
        //    输出格式:"E001-张三-12000.0-技术部"
    
        // 3. 通讯录按姓名排序:重新创建 TreeSet,传入按姓名排序的 Comparator
        //    (可用匿名内部类,也可尝试用 Lambda:Comparator.comparing(Employee::getName) 或
        //    (o1, o2) -> o1.getName().compareTo(o2.getName()))
        //    迭代器遍历输出
    }
    }
    

预期输出:

========== 默认排序:按薪资降序(薪资相同按工号) ==========
E002-李四-15000.0-市场部
E003-王五-15000.0-技术部
E001-张三-12000.0-技术部
E005-刘七-12000.0-市场部
E004-赵六-9000.0-财务部
========== 通讯录:按姓名排序 ==========
E002-李四-15000.0-市场部
E005-刘七-12000.0-市场部
E003-王五-15000.0-技术部
E001-张三-12000.0-技术部
E004-赵六-9000.0-财务部

原理分析(必答,写在代码注释或提交说明中):

  1. 为什么 E002 添加两次却只存了一个?—— compareTo 工号相同返回 0,视为重复,第二次 add 不生效
  2. 为什么 equals / hashCode 要以工号为判定依据?—— 与 compareTo 判定口径一致,官方推荐 compareTo == 0 → equals 返回 true、hashCode 一致
  3. 临时按姓名排序为什么不用修改 Employee 类?—— 用 Comparator(匿名内部类 / Lambda),排序规则在集合这边

思考题(选做,不要求提交代码):

  1. Collections.sort 和 TreeSet 自动排序有什么区别?(提示:前者改变已有 List 顺序,后者存入时排序且去重)
  2. 用 Lambda 重写按姓名排序的 Comparator 怎么写?(提示:(o1, o2) -> o1.getName().compareTo(o2.getName()))
  3. 如果 TreeSet 添加的员工工号和薪资都相同但姓名不同,能都存入吗?equals 会返回 true 吗?(提示:compareTo 返回 0 视为重复不存;equals 以工号判定也会返回 true——口径一致)

第三部分:简答题(必做)

简答题:Lambda 表达式与匿名内部类的区别

请用文字说明(可配合示例代码)回答以下问题:

  1. Lambda 表达式的标准格式和三要素是什么? (形式参数)->{代码块} 各部分分别对应什么?
  2. Lambda 有哪些省略规则? 请各举一个例子说明。
  3. Lambda 和匿名内部类有什么区别? 请从以下三个维度回答:
    • 需要的类型(接口 / 抽象类 / 普通类)
    • 使用限制(抽象方法个数)
    • 编译原理(是否产生独立的 .class 文件)

答题要点提示:

  • 标准格式:(形式参数) -> {代码块};三要素:形式参数、箭头、代码块
  • 三条省略规则:参数类型可全省(不能只省一个);单参数可省小括号;单语句可省大括号/分号/return
  • 区别:
    • 需要的类型:匿名内部类可以是接口、抽象类、普通具体类;Lambda 只能是接口
    • 使用限制:接口有多个抽象方法只能用匿名内部类;Lambda 要求有且只有一个抽象方法(函数式接口)
    • 编译原理:匿名内部类编译产生独立的 .class 文件;Lambda 编译不产生 .class 文件,字节码在运行期动态生成