说明:本次作业为阶段性综合练习,覆盖近期核心知识点——Java 集合框架(ArrayList / LinkedList / HashMap / TreeMap / HashSet / TreeSet 的新建、增删改查、遍历、迭代器)、匿名内部类(Comparator 排序)与异常处理(自定义异常 + 集合综合应用),并附 1 道简答题(HashMap 底层原理)。
题目分为 ⭐ 基础(必做):作业 1~3;⭐⭐ 进阶(必做):作业 4~5;简答题(必做)。请新建 Java 项目,编写并运行代码,将运行结果截图提交。
知识点:ArrayList / LinkedList 创建、增删改查、三种遍历方式(for+get / Iterator 迭代器 / 增强 for)
场景描述:使用 ArrayList 和 LinkedList 分别存储学生名单,练习集合的增删改查与三种遍历方式,体会 List 系集合的通用操作。
题目要求:
在 ListDemo 类的 main 方法中,完成以下操作:
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 迭代器遍历 ==========
张三 李思 王五 孙七
思考题(选做,不要求提交代码):
for + get(i) 遍历为什么效率低?(提示:每次 get(i) 从头遍历,O(n²))Iterator 的 hasNext() 和 next() 分别做什么?指针如何移动?知识点:HashMap / TreeMap 创建、put() / get() / containsKey() / remove() / keySet() / entrySet()、Map 两种遍历方式
场景描述:用 Map 存储学生成绩(学号 → 成绩)。HashMap 无序,TreeMap 按键自动排序——对比两者遍历顺序的差异。
题目要求:
在 MapDemo 类的 main 方法中,完成以下操作:
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
思考题(选做,不要求提交代码):
keySet() 遍历和 entrySet() 遍历的效率差异?为什么?keySet() 返回的是 Set 而不是 List?(提示:键不可重复)知识点:HashSet 去重、TreeSet 自动排序、迭代器 / 增强 for 遍历
场景描述:Set 集合无序、不可重复。HashSet 去重(无序),TreeSet 自动排序(有序)。模拟水果清单去重与数字排序。
题目要求:
在 SetDemo 类的 main 方法中,完成以下操作:
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
思考题(选做,不要求提交代码):
get(index)?如何遍历 Set?(提示:无序无索引,用迭代器/增强 for)知识点:try-catch 多 catch 块、自定义异常、异常处理 + ArrayList 综合应用、迭代器遍历
场景描述:编写成绩统计程序,把输入的成绩存入 ArrayList,计算总分和平均分。要求处理三类异常:输入不是整数(InputMismatchException)、成绩不合法(自定义异常 InvalidScoreException)、索引越界(IndexOutOfBoundsException)。
题目要求:
先定义自定义异常类:
// InvalidScoreException.java
public class InvalidScoreException extends RuntimeException {
public InvalidScoreException(String message) {
super(message);
}
}
再编写 ScoreStatisticsDemo:
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() 清空输入缓冲,否则会死循环InputMismatchException 在 Exception 之前)while 循环实现"输入错误就重试"的鲁棒性思考题(选做,不要求提交代码):
Exception(编译期)和 RuntimeException(运行期)有什么区别?nextLine() 清空缓冲?知识点:匿名内部类实现 Comparator、Collections.sort()、TreeSet 自定义排序、List / Set 排序
场景描述:定义 Student 类(name + age + score),用匿名内部类实现 Comparator 接口,对集合中的学生按年龄、成绩、姓名排序。体会匿名内部类"临时定义一个实现类"的便捷性。
题目要求:
先定义 Student 类:
// 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:
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}
思考题(选做,不要求提交代码):
Comparator 的 compare() 返回负数 / 0 / 正数分别代表什么?Comparable 和 Comparator 的区别是什么?(提示:类自身排序 vs 外部排序器)请用文字说明(可配合画图)回答以下问题:
TREEIFY_THRESHOLD、MIN_TREEIFY_CAPACITY、UNTREEIFY_THRESHOLD)答题要点提示:
hashCode() 定位,再 equals() 比较homework0731@author、@title、@create 文档注释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();
}
}
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));
}
}
}
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();
}
}
// InvalidScoreException.java
public class InvalidScoreException extends RuntimeException {
public InvalidScoreException(String message) {
super(message);
}
}
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()));
}
}
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 的底层原理:
hashCode() 和 equals() 保证键唯一。先调用 hashCode() 计算哈希值、定位数组下标;若该位置已有元素,再调用 equals() 比较——相同则视为同一个键(覆盖),不同则挂在链表上。TREEIFY_THRESHOLD)且数组长度 > 64(MIN_TREEIFY_CAPACITY)两个条件同时满足时,链表转为红黑树;节点长度 < 6(UNTREEIFY_THRESHOLD)时退化回链表。1 << 4),负载因子 0.75f。当已存储元素个数超过「容量 × 负载因子」(16 × 0.75 = 12)时触发扩容,扩容后容量约为原来的 2 倍。