说明:本次作业围绕今日授课核心内容——比较器排序(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
⚠ 注意:
- 包名必须严格按题目要求书写(全小写),目录结构必须与包结构一致
- 每个 Java 文件头部需包含
@author、@title、@create文档注释- 简答题(Lambda 与匿名内部类)请写在 Word / TXT 文档中,与代码一起提交
包名:p1_comparable
知识点:Comparable<T> 接口、compareTo(T o) 重写、返回值规则(负数存左 / 0 重复不存 / 正数存右)、二次比较、TreeSet 自动排序 + 去重
场景描述:编写学生成绩排行榜。Student 类实现 Comparable(按成绩降序,成绩相同再按姓名),存入 TreeSet 后自动排序并自动去重。
题目要求:
定义 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;
}
}
编写测试类 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
思考题(选做,不要求提交代码):
compareTo 做了二次比较(姓名),返回值不为 0compareTo 只按成绩比较,会出现什么现象?(提示:同分视为重复,后一个不存)包名:p2_comparator
知识点:Collections.sort(list, comparator)、匿名内部类实现 Comparator、Double.compare、降序技巧(交换参数)、Collections.max/min/binarySearch
场景描述:Product 类(名称 + 价格 + 库存)不实现 Comparable。用 Collections.sort + 匿名内部类 Comparator 对商品进行多种临时排序,并练习 Collections 工具类的查找方法。
题目要求:
定义 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 方法
}
编写 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
思考题(选做,不要求提交代码):
(int)(p1.getPrice() - p2.getPrice())?(提示:浮点精度 + 强转截断)Collections.binarySearch 使用前提是什么?(提示:查找前必须先按相同规则排序)包名:p3_varargs
知识点:可变参数 int... a、for each 遍历、可变参数本质即数组、固定参数 + 可变参数(可变参数置末)、求总分/平均分/最高分
场景描述:用可变参数实现"任意个数成绩的统计"——一个方法接收 2 个、3 个、5 个...任意个数的成绩,并计算总分、平均分、最高分。
题目要求:
定义 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; // 请补全
}
}
定义 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
思考题(选做,不要求提交代码):
int... a 和 int[] a 是什么关系?为什么调用效果一样?(提示:可变参数是数组的语法糖)showScore(String name, int... scores) 中可变参数能写在 name 前面吗?(提示:可变参数必须放在参数列表最后)showScore("王五")(不传成绩),scores.length 是多少?会不会报错?包名:p4_lambda
知识点:函数式接口、Lambda 标准格式 (参数)->{代码块}、三条省略规则(类型可全省 / 单参数省小括号 / 单语句省大括号分号 return)、Collections.sort + Lambda
场景描述:今日课上新学了 Lambda 表达式——它是另一种形式的匿名内部类,能大幅简化代码。要求用 Lambda 实现两个函数式接口,并用 Lambda 改写 Collections.sort 的排序代码,体会省略模式的便捷。
题目要求:
定义两个函数式接口:
// Addable.java —— 有参有返回值
public interface Addable {
int add(int x, int y);
}
// Flyable.java —— 有参无返回值
public interface Flyable {
void fly(String s);
}
编写 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) -> ...);
}
}
进阶挑战:定义一个 Product 类(名称 + 价格),用 Lambda + Collections.sort 按价格降序排序输出(提示:Double.compare(s2.getPrice(), s1.getPrice()))。
预期输出:
30
今天天气不错...是风和日丽的...
长翅膀....飞...
========== Collections.sort 用 Lambda 排序 ==========
(按你的学生/商品数据排序输出)
思考题(选做,不要求提交代码):
() 才能唯一确定指向它)包名:p5_employee
知识点:Comparable 自然排序 + equals/hashCode 一致性 + TreeSet 自动排序/去重 + 匿名内部类或 Lambda 按姓名排序 + 迭代器遍历
场景描述:某公司员工信息管理系统。Employee 类(工号、姓名、薪资、部门)要求:
题目要求:
定义 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)
}
编写 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-财务部
原理分析(必答,写在代码注释或提交说明中):
compareTo 工号相同返回 0,视为重复,第二次 add 不生效equals / hashCode 要以工号为判定依据?—— 与 compareTo 判定口径一致,官方推荐 compareTo == 0 → equals 返回 true、hashCode 一致思考题(选做,不要求提交代码):
Collections.sort 和 TreeSet 自动排序有什么区别?(提示:前者改变已有 List 顺序,后者存入时排序且去重)(o1, o2) -> o1.getName().compareTo(o2.getName()))equals 会返回 true 吗?(提示:compareTo 返回 0 视为重复不存;equals 以工号判定也会返回 true——口径一致)请用文字说明(可配合示例代码)回答以下问题:
(形式参数)->{代码块} 各部分分别对应什么?答题要点提示:
(形式参数) -> {代码块};三要素:形式参数、箭头、代码块