|
|
@@ -0,0 +1,665 @@
|
|
|
+# 2026年7月31日 课后作业 — 集合综合运用(含匿名内部类与异常)
|
|
|
+
|
|
|
+> **说明**:本次作业为**阶段性综合练习**,覆盖近期核心知识点——**Java 集合框架**(ArrayList / LinkedList / HashMap / TreeMap / HashSet / TreeSet 的新建、增删改查、遍历、迭代器)、**匿名内部类**(Comparator 排序)与**异常处理**(自定义异常 + 集合综合应用),并附 1 道简答题(HashMap 底层原理)。
|
|
|
+>
|
|
|
+> 题目分为 **⭐ 基础(必做)**:作业 1~3;**⭐⭐ 进阶(必做)**:作业 4~5;**简答题(必做)**。请新建 Java 项目,编写并运行代码,将运行结果截图提交。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第一部分:基础题(⭐ 必做)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 作业 1:ArrayList 与 LinkedList 基本操作 — 学生名单管理
|
|
|
+
|
|
|
+**知识点**:`ArrayList` / `LinkedList` 创建、增删改查、三种遍历方式(`for+get` / `Iterator` 迭代器 / 增强 for)
|
|
|
+
|
|
|
+**场景描述**:使用 ArrayList 和 LinkedList 分别存储学生名单,练习集合的**增删改查**与**三种遍历方式**,体会 List 系集合的通用操作。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `ListDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class ListDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 ArrayList<String> 和 LinkedList<String>,各添加 5 个姓名:
|
|
|
+ // "张三", "李四", "王五", "赵六", "孙七"
|
|
|
+
|
|
|
+ // 2. 输出两个集合的大小(size()):"ArrayList 大小:5 / LinkedList 大小:5"
|
|
|
+
|
|
|
+ // 3. 查询:获取并输出索引 2 的元素(get(2)):"索引 2 的学生:王五"
|
|
|
+
|
|
|
+ // 4. 修改:将索引 1 的元素改为"李思"(set())
|
|
|
+
|
|
|
+ // 5. 删除:移除索引 3 的元素(remove(index)),输出被删除的元素
|
|
|
+
|
|
|
+ // 6. 遍历:分别用【三种方式】遍历 ArrayList:
|
|
|
+ // ① for + size() + get(i)
|
|
|
+ // ② Iterator 迭代器(while + hasNext() + next())
|
|
|
+ // ③ 增强 for(foreach)
|
|
|
+
|
|
|
+ // 7. 用 Iterator 迭代器遍历 LinkedList,输出所有元素
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+ArrayList 大小:5
|
|
|
+LinkedList 大小:5
|
|
|
+索引 2 的学生:王五
|
|
|
+已修改索引 1:李四 → 李思
|
|
|
+已删除索引 3:赵六
|
|
|
+========== 方式①:for + get ==========
|
|
|
+张三 李思 王五 孙七
|
|
|
+========== 方式②:Iterator 迭代器 ==========
|
|
|
+张三 李思 王五 孙七
|
|
|
+========== 方式③:增强 for ==========
|
|
|
+张三 李思 王五 孙七
|
|
|
+========== LinkedList 迭代器遍历 ==========
|
|
|
+张三 李思 王五 孙七
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**(选做,不要求提交代码):
|
|
|
+1. LinkedList 用 `for + get(i)` 遍历为什么效率低?(提示:每次 `get(i)` 从头遍历,O(n²))
|
|
|
+2. `Iterator` 的 `hasNext()` 和 `next()` 分别做什么?指针如何移动?
|
|
|
+3. 三种遍历方式各自的适用场景是什么?迭代器和增强 for 是什么关系?
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 作业 2:HashMap 与 TreeMap 基本操作 — 学生成绩表
|
|
|
+
|
|
|
+**知识点**:`HashMap` / `TreeMap` 创建、`put()` / `get()` / `containsKey()` / `remove()` / `keySet()` / `entrySet()`、Map 两种遍历方式
|
|
|
+
|
|
|
+**场景描述**:用 Map 存储学生成绩(学号 → 成绩)。**HashMap 无序**,**TreeMap 按键自动排序**——对比两者遍历顺序的差异。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `MapDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class MapDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 HashMap<String, Integer>,添加 4 组成绩:
|
|
|
+ // "x001"->90, "x002"->85, "x003"->92, "x004"->78
|
|
|
+
|
|
|
+ // 2. 输出集合大小(size()):"学生人数:4"
|
|
|
+
|
|
|
+ // 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(),输出所有"学号-成绩"
|
|
|
+
|
|
|
+ // 9. 创建 TreeMap<String, Integer>,放入同样的 4 组数据
|
|
|
+ // 用 keySet() 遍历输出,观察:TreeMap 的遍历顺序与 HashMap 有什么不同?
|
|
|
+ // —— 提示:TreeMap 按键(学号)自动排序
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**(HashMap 部分顺序不定,TreeMap 按键排序):
|
|
|
+```
|
|
|
+学生人数:4
|
|
|
+学号 x002 的成绩:85
|
|
|
+包含键 x001:true
|
|
|
+包含键 x999:false
|
|
|
+修改后 x004 的成绩:88
|
|
|
+删除 x001 后,学生人数:3
|
|
|
+========== HashMap 方式一:keySet + get ==========
|
|
|
+学号:x002,成绩:85
|
|
|
+学号:x003,成绩:92
|
|
|
+学号:x004,成绩:88
|
|
|
+========== HashMap 方式二:entrySet ==========
|
|
|
+学号:x002,成绩:85
|
|
|
+学号:x003,成绩:92
|
|
|
+学号:x004,成绩:88
|
|
|
+========== TreeMap keySet 遍历(按键排序) ==========
|
|
|
+学号:x002,成绩:85
|
|
|
+学号:x003,成绩:92
|
|
|
+学号:x004,成绩:88
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**(选做,不要求提交代码):
|
|
|
+1. HashMap 和 TreeMap 的遍历顺序有什么不同?为什么?(提示:哈希定位 vs 红黑树中序排序)
|
|
|
+2. `keySet()` 遍历和 `entrySet()` 遍历的效率差异?为什么?
|
|
|
+3. 为什么 `keySet()` 返回的是 Set 而不是 List?(提示:键不可重复)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 作业 3:HashSet 与 TreeSet 基本操作 — 单词去重与排序
|
|
|
+
|
|
|
+**知识点**:`HashSet` 去重、`TreeSet` 自动排序、迭代器 / 增强 for 遍历
|
|
|
+
|
|
|
+**场景描述**:Set 集合**无序、不可重复**。HashSet 去重(无序),TreeSet 自动排序(有序)。模拟水果清单去重与数字排序。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `SetDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class SetDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 HashSet<String>,依次添加:香蕉, 苹果, 葡萄, 苹果, 西瓜, 香蕉
|
|
|
+ // 观察输出:重复元素"苹果""香蕉"只保留一个(自动去重)
|
|
|
+
|
|
|
+ // 2. 输出集合大小:应输出 4(6 个元素去重后剩 4 个)
|
|
|
+
|
|
|
+ // 3. 用 Iterator 迭代器遍历输出所有元素
|
|
|
+
|
|
|
+ // 4. 创建 TreeSet<String>,添加同样的水果
|
|
|
+ // 观察输出:自动按【字典序】排序
|
|
|
+
|
|
|
+ // 5. 创建 TreeSet<Integer>,添加:50, 20, 80, 10, 60
|
|
|
+ // 观察输出:自动按【数值升序】排序
|
|
|
+
|
|
|
+ // 6. 用增强 for 遍历 TreeSet<Integer>,输出所有元素
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**(HashSet 顺序不定):
|
|
|
+```
|
|
|
+========== HashSet 去重 ==========
|
|
|
+集合大小:4
|
|
|
+迭代器遍历:西瓜 香蕉 苹果 葡萄
|
|
|
+========== TreeSet<String> 字典序排序 ==========
|
|
|
+苹果 葡萄 西瓜 香蕉
|
|
|
+========== TreeSet<Integer> 数值升序 ==========
|
|
|
+10 20 50 60 80
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**(选做,不要求提交代码):
|
|
|
+1. HashSet 和 TreeSet 的区别是什么?(提示:哈希去重 vs 红黑树排序)
|
|
|
+2. Set 为什么没有 `get(index)`?如何遍历 Set?(提示:无序无索引,用迭代器/增强 for)
|
|
|
+3. 自定义对象放进 TreeSet 会怎样?需要满足什么条件?(提示:实现 Comparable 或传入 Comparator)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第二部分:进阶题(⭐⭐ 必做)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 作业 4:异常处理 + 集合综合 — 学生成绩统计系统
|
|
|
+
|
|
|
+**知识点**:`try-catch` 多 catch 块、自定义异常、异常处理 + ArrayList 综合应用、迭代器遍历
|
|
|
+
|
|
|
+**场景描述**:编写成绩统计程序,把输入的成绩存入 `ArrayList`,计算总分和平均分。要求处理三类异常:**输入不是整数**(`InputMismatchException`)、**成绩不合法**(自定义异常 `InvalidScoreException`)、**索引越界**(`IndexOutOfBoundsException`)。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+先定义自定义异常类:
|
|
|
+
|
|
|
+```java
|
|
|
+// InvalidScoreException.java
|
|
|
+public class InvalidScoreException extends RuntimeException {
|
|
|
+ public InvalidScoreException(String message) {
|
|
|
+ super(message);
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+再编写 `ScoreStatisticsDemo`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.InputMismatchException;
|
|
|
+import java.util.Scanner;
|
|
|
+
|
|
|
+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); }
|
|
|
+
|
|
|
+ // 2. 使用 try-catch 处理以下异常:
|
|
|
+ // a. 输入不是整数 → InputMismatchException,提示"请输入整数"并要求重新输入
|
|
|
+ // b. 成绩不在 0~100 → 抛出 InvalidScoreException(自定义异常)
|
|
|
+ // if (s < 0 || s > 100) { throw new InvalidScoreException("成绩必须在0~100之间"); }
|
|
|
+ // c. 下标越界 → IndexOutOfBoundsException
|
|
|
+
|
|
|
+ // 3. 计算总分和平均分(用 for 或迭代器遍历求和)
|
|
|
+
|
|
|
+ // 4. 使用迭代器遍历输出所有成绩
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**运行效果示意**:
|
|
|
+```
|
|
|
+请输入第 1 个成绩(0~100):abc
|
|
|
+输入错误,请输入整数!
|
|
|
+请输入第 1 个成绩(0~100):85
|
|
|
+请输入第 2 个成绩(0~100):92
|
|
|
+请输入第 3 个成绩(0~100):120
|
|
|
+成绩不合法:成绩必须在0~100之间
|
|
|
+请输入第 3 个成绩(0~100):78
|
|
|
+请输入第 4 个成绩(0~100):66
|
|
|
+请输入第 5 个成绩(0~100):90
|
|
|
+========== 成绩统计结果 ==========
|
|
|
+成绩列表:85 92 78 66 90
|
|
|
+总分:411,平均分:82.2
|
|
|
+```
|
|
|
+
|
|
|
+**提示**:
|
|
|
+- `InputMismatchException` 发生后需要调用 `sc.nextLine()` 清空输入缓冲,否则会死循环
|
|
|
+- 多 catch 块中,**子类异常在前,父类异常在后**(如 `InputMismatchException` 在 `Exception` 之前)
|
|
|
+- 可以结合 `while` 循环实现"输入错误就重试"的鲁棒性
|
|
|
+
|
|
|
+**思考题**(选做,不要求提交代码):
|
|
|
+1. 多 catch 块的书写顺序有什么要求?为什么子类必须在前?
|
|
|
+2. 自定义异常继承 `Exception`(编译期)和 `RuntimeException`(运行期)有什么区别?
|
|
|
+3. 为什么输入不是整数后要调用 `nextLine()` 清空缓冲?
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 作业 5:匿名内部类 + 集合排序 — 学生排序系统
|
|
|
+
|
|
|
+**知识点**:**匿名内部类**实现 `Comparator`、`Collections.sort()`、`TreeSet` 自定义排序、List / Set 排序
|
|
|
+
|
|
|
+**场景描述**:定义 `Student` 类(name + age + score),用**匿名内部类**实现 `Comparator` 接口,对集合中的学生按年龄、成绩、姓名排序。体会匿名内部类"临时定义一个实现类"的便捷性。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+先定义 `Student` 类:
|
|
|
+
|
|
|
+```java
|
|
|
+// Student.java
|
|
|
+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 方法
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+再编写 `SortDemo`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class SortDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建 ArrayList<Student>,添加 4 名学生:
|
|
|
+ // 张三 20 85 / 李四 22 90 / 王五 19 78 / 赵六 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. 输出按年龄排序后的结果
|
|
|
+
|
|
|
+ // 4. 再写一个匿名内部类 Comparator,按成绩(score)降序排序,输出结果
|
|
|
+ // 提示:成绩是 double,比较可用 Double.compare(s2.getScore(), s1.getScore())
|
|
|
+
|
|
|
+ // 5. 创建一个 TreeSet<Student>,传入按姓名(name)排序的匿名 Comparator
|
|
|
+ // 把 4 名学生放入 TreeSet,用迭代器遍历输出,观察自动排序效果
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+========== 按年龄升序排序 ==========
|
|
|
+Student{name='王五', age=19, score=78.0}
|
|
|
+Student{name='张三', age=20, score=85.0}
|
|
|
+Student{name='赵六', age=21, score=88.0}
|
|
|
+Student{name='李四', age=22, score=90.0}
|
|
|
+========== 按成绩降序排序 ==========
|
|
|
+Student{name='李四', age=22, score=90.0}
|
|
|
+Student{name='赵六', age=21, score=88.0}
|
|
|
+Student{name='张三', age=20, score=85.0}
|
|
|
+Student{name='王五', age=19, score=78.0}
|
|
|
+========== TreeSet 按姓名排序 ==========
|
|
|
+Student{name='张三', age=20, score=85.0}
|
|
|
+Student{name='李四', age=22, score=90.0}
|
|
|
+Student{name='王五', age=19, score=78.0}
|
|
|
+Student{name='赵六', age=21, score=88.0}
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**(选做,不要求提交代码):
|
|
|
+1. 匿名内部类的语法格式是什么?这里为什么可以用匿名内部类?(提示:Comparator 是接口)
|
|
|
+2. `Comparator` 的 `compare()` 返回负数 / 0 / 正数分别代表什么?
|
|
|
+3. `Comparable` 和 `Comparator` 的区别是什么?(提示:类自身排序 vs 外部排序器)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第三部分:简答题(必做)
|
|
|
+
|
|
|
+## 简答题:说明 HashMap 的底层原理
|
|
|
+
|
|
|
+请用文字说明(可配合画图)回答以下问题:
|
|
|
+
|
|
|
+1. **HashMap 的数据结构是什么?** JDK 1.7 和 JDK 1.8 有什么区别?
|
|
|
+2. **HashMap 是如何保证键 Key 的唯一性的?** 依赖哪两个方法?
|
|
|
+3. **什么是哈希冲突?** HashMap 是如何解决的?
|
|
|
+4. **HashMap 的树化条件和退化条件分别是什么?**(提示:`TREEIFY_THRESHOLD`、`MIN_TREEIFY_CAPACITY`、`UNTREEIFY_THRESHOLD`)
|
|
|
+5. **HashMap 的默认容量和负载因子是多少?** 负载因子的作用是什么?
|
|
|
+
|
|
|
+**答题要点提示**:
|
|
|
+- 数据结构:**数组 + 链表**(JDK1.7);**数组 + 链表 / 数组 + 红黑树**(JDK1.8)
|
|
|
+- 键唯一性:先 `hashCode()` 定位,再 `equals()` 比较
|
|
|
+- 哈希冲突:多个键的哈希值相同 → 挂到同一位置的链表上(链地址法)
|
|
|
+- 树化条件:**链表节点长度 > 8 且 数组长度 > 64**(两个条件同时满足);退化条件:**节点长度 < 6**
|
|
|
+- 默认容量 **16**,负载因子 **0.75f**:已存元素超过容量 × 0.75 时触发扩容
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 提交要求
|
|
|
+
|
|
|
+1. 新建 Java 项目(或使用现有项目),包名建议:`homework0731`
|
|
|
+2. 完成作业 1~5 的编程题,每个类文件头部包含 `@author`、`@title`、`@create` 文档注释
|
|
|
+3. 简答题写在 Word / TXT 文档中(或写在代码注释里)
|
|
|
+4. 运行代码,控制台输出需与预期输出一致
|
|
|
+5. **将代码源文件和运行结果截图**提交到班级群
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 参考答案(供批改参考)
|
|
|
+
|
|
|
+## 作业 1 参考
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class ListDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ ArrayList<String> arrayList = new ArrayList<>();
|
|
|
+ LinkedList<String> linkedList = new LinkedList<>();
|
|
|
+ String[] names = {"张三", "李四", "王五", "赵六", "孙七"};
|
|
|
+ for (String name : names) {
|
|
|
+ arrayList.add(name);
|
|
|
+ linkedList.add(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("ArrayList 大小:" + arrayList.size());
|
|
|
+ System.out.println("LinkedList 大小:" + linkedList.size());
|
|
|
+ System.out.println("索引 2 的学生:" + arrayList.get(2));
|
|
|
+
|
|
|
+ arrayList.set(1, "李思");
|
|
|
+ System.out.println("已修改索引 1:李四 → 李思");
|
|
|
+ System.out.println("已删除索引 3:" + arrayList.remove(3));
|
|
|
+
|
|
|
+ System.out.println("========== 方式①:for + get ==========");
|
|
|
+ for (int i = 0; i < arrayList.size(); i++) {
|
|
|
+ System.out.print(arrayList.get(i) + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+
|
|
|
+ System.out.println("========== 方式②:Iterator 迭代器 ==========");
|
|
|
+ Iterator<String> iterator = arrayList.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ System.out.print(iterator.next() + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+
|
|
|
+ System.out.println("========== 方式③:增强 for ==========");
|
|
|
+ for (String name : arrayList) {
|
|
|
+ System.out.print(name + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+
|
|
|
+ System.out.println("========== LinkedList 迭代器遍历 ==========");
|
|
|
+ Iterator<String> iterator1 = linkedList.iterator();
|
|
|
+ while (iterator1.hasNext()) {
|
|
|
+ System.out.print(iterator1.next() + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 作业 2 参考
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class MapDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ HashMap<String, Integer> map = new HashMap<>();
|
|
|
+ map.put("x001", 90);
|
|
|
+ map.put("x002", 85);
|
|
|
+ map.put("x003", 92);
|
|
|
+ map.put("x004", 78);
|
|
|
+
|
|
|
+ System.out.println("学生人数:" + map.size());
|
|
|
+ System.out.println("学号 x002 的成绩:" + map.get("x002"));
|
|
|
+ System.out.println("包含键 x001:" + map.containsKey("x001"));
|
|
|
+ System.out.println("包含键 x999:" + map.containsKey("x999"));
|
|
|
+
|
|
|
+ map.put("x004", 88); // 键已存在 → 覆盖旧值
|
|
|
+ System.out.println("修改后 x004 的成绩:" + map.get("x004"));
|
|
|
+ map.remove("x001");
|
|
|
+ System.out.println("删除 x001 后,学生人数:" + map.size());
|
|
|
+
|
|
|
+ System.out.println("========== HashMap 方式一:keySet + get ==========");
|
|
|
+ Set<String> keys = map.keySet();
|
|
|
+ for (String key : keys) {
|
|
|
+ System.out.println("学号:" + key + ",成绩:" + map.get(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("========== HashMap 方式二:entrySet ==========");
|
|
|
+ Set<Map.Entry<String, Integer>> entries = map.entrySet();
|
|
|
+ for (Map.Entry<String, Integer> me : entries) {
|
|
|
+ System.out.println("学号:" + me.getKey() + ",成绩:" + me.getValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("========== TreeMap keySet 遍历(按键排序) ==========");
|
|
|
+ TreeMap<String, Integer> treeMap = new TreeMap<>();
|
|
|
+ treeMap.put("x001", 90);
|
|
|
+ treeMap.put("x002", 85);
|
|
|
+ treeMap.put("x003", 92);
|
|
|
+ treeMap.put("x004", 78);
|
|
|
+ for (String key : treeMap.keySet()) {
|
|
|
+ System.out.println("学号:" + key + ",成绩:" + treeMap.get(key));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 作业 3 参考
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class SetDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ System.out.println("========== HashSet 去重 ==========");
|
|
|
+ HashSet<String> hashSet = new HashSet<>();
|
|
|
+ hashSet.add("香蕉");
|
|
|
+ hashSet.add("苹果");
|
|
|
+ hashSet.add("葡萄");
|
|
|
+ hashSet.add("苹果");
|
|
|
+ hashSet.add("西瓜");
|
|
|
+ hashSet.add("香蕉");
|
|
|
+ System.out.println("集合大小:" + hashSet.size());
|
|
|
+
|
|
|
+ System.out.print("迭代器遍历:");
|
|
|
+ Iterator<String> iterator = hashSet.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ System.out.print(iterator.next() + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+
|
|
|
+ System.out.println("========== TreeSet<String> 字典序排序 ==========");
|
|
|
+ TreeSet<String> treeSet = new TreeSet<>();
|
|
|
+ treeSet.add("香蕉");
|
|
|
+ treeSet.add("苹果");
|
|
|
+ treeSet.add("葡萄");
|
|
|
+ treeSet.add("西瓜");
|
|
|
+ for (String fruit : treeSet) {
|
|
|
+ System.out.print(fruit + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+
|
|
|
+ System.out.println("========== TreeSet<Integer> 数值升序 ==========");
|
|
|
+ TreeSet<Integer> numSet = new TreeSet<>();
|
|
|
+ numSet.add(50);
|
|
|
+ numSet.add(20);
|
|
|
+ numSet.add(80);
|
|
|
+ numSet.add(10);
|
|
|
+ numSet.add(60);
|
|
|
+ for (Integer num : numSet) {
|
|
|
+ System.out.print(num + " ");
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 作业 4 参考
|
|
|
+
|
|
|
+```java
|
|
|
+// InvalidScoreException.java
|
|
|
+public class InvalidScoreException extends RuntimeException {
|
|
|
+ public InvalidScoreException(String message) {
|
|
|
+ super(message);
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```java
|
|
|
+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<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < 5; i++) {
|
|
|
+ while (true) {
|
|
|
+ try {
|
|
|
+ System.out.print("请输入第 " + (i + 1) + " 个成绩(0~100):");
|
|
|
+ int s = sc.nextInt();
|
|
|
+ if (s < 0 || s > 100) {
|
|
|
+ throw new InvalidScoreException("成绩必须在0~100之间");
|
|
|
+ }
|
|
|
+ scores.add(s);
|
|
|
+ break;
|
|
|
+ } catch (InputMismatchException e) {
|
|
|
+ System.out.println("输入错误,请输入整数!");
|
|
|
+ sc.nextLine(); // 清空输入缓冲,防止死循环
|
|
|
+ } catch (InvalidScoreException e) {
|
|
|
+ System.out.println("成绩不合法:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("========== 成绩统计结果 ==========");
|
|
|
+ int total = 0;
|
|
|
+ Iterator<Integer> iterator = scores.iterator();
|
|
|
+ System.out.print("成绩列表:");
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ int s = iterator.next();
|
|
|
+ System.out.print(s + " ");
|
|
|
+ total += s;
|
|
|
+ }
|
|
|
+ System.out.println();
|
|
|
+ System.out.println("总分:" + total + ",平均分:" + (total * 1.0 / scores.size()));
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 作业 5 参考
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+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));
|
|
|
+
|
|
|
+ // 按年龄升序(匿名内部类)
|
|
|
+ Collections.sort(list, new Comparator<Student>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Student s1, Student s2) {
|
|
|
+ return s1.getAge() - s2.getAge();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ System.out.println("========== 按年龄升序排序 ==========");
|
|
|
+ for (Student s : list) {
|
|
|
+ System.out.println(s);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按成绩降序(匿名内部类)
|
|
|
+ Collections.sort(list, new Comparator<Student>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Student s1, Student s2) {
|
|
|
+ return Double.compare(s2.getScore(), s1.getScore());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ System.out.println("========== 按成绩降序排序 ==========");
|
|
|
+ for (Student s : list) {
|
|
|
+ System.out.println(s);
|
|
|
+ }
|
|
|
+
|
|
|
+ // TreeSet 按姓名排序(匿名内部类 Comparator)
|
|
|
+ TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
|
|
|
+ @Override
|
|
|
+ public int compare(Student s1, Student s2) {
|
|
|
+ return s1.getName().compareTo(s2.getName());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ treeSet.add(new Student("张三", 20, 85));
|
|
|
+ treeSet.add(new Student("李四", 22, 90));
|
|
|
+ treeSet.add(new Student("王五", 19, 78));
|
|
|
+ treeSet.add(new Student("赵六", 21, 88));
|
|
|
+ System.out.println("========== TreeSet 按姓名排序 ==========");
|
|
|
+ Iterator<Student> iterator = treeSet.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ System.out.println(iterator.next());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 简答题参考要点
|
|
|
+
|
|
|
+**HashMap 的底层原理**:
|
|
|
+
|
|
|
+1. **数据结构**:HashMap 底层是**哈希表**,本质是「**数组 + 链表**」;JDK 1.8 之后引入了红黑树,在满足条件时链表会**树化**为「数组 + 红黑树」。
|
|
|
+2. **键唯一性**:HashMap 依赖 `hashCode()` 和 `equals()` 保证键唯一。先调用 `hashCode()` 计算哈希值、定位数组下标;若该位置已有元素,再调用 `equals()` 比较——相同则视为同一个键(覆盖),不同则挂在链表上。
|
|
|
+3. **哈希冲突**:多个不同键计算出相同哈希值(下标)时发生冲突,HashMap 采用**链地址法**解决——冲突的元素以链表形式挂在同一数组位置上。
|
|
|
+4. **树化/退化条件**:链表节点长度 **> 8**(`TREEIFY_THRESHOLD`)且数组长度 **> 64**(`MIN_TREEIFY_CAPACITY`)两个条件**同时满足**时,链表转为红黑树;节点长度 **< 6**(`UNTREEIFY_THRESHOLD`)时退化回链表。
|
|
|
+5. **默认容量与负载因子**:默认初始容量 **16**(`1 << 4`),负载因子 **0.75f**。当已存储元素个数超过「容量 × 负载因子」(16 × 0.75 = 12)时触发扩容,扩容后容量约为原来的 2 倍。
|
|
|
+
|