Эх сурвалжийг харах

feat: 新增20260729课后作业——ArrayList源码逐行注释分析(构造方法/扩容/增删改) + 手写MyArrayList选做题;同步exercise03练习3参考答案(年龄统计+冒泡排序)

WanJL 4 цаг өмнө
parent
commit
6e13967b5e

+ 90 - 0
授课代码/c260729/src/exericse/exericse03/Student.java

@@ -0,0 +1,90 @@
+package exericse.exericse03;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description 学生类
+ * @create 2026/7/29
+ */
+public class Student {
+    // 属性
+    private String name;      // 姓名
+    private Integer age;      // 年龄(使用包装类)
+    private String studentId; // 学号
+
+    /**
+     * 无参构造方法
+     */
+    public Student() {
+    }
+
+    /**
+     * 全参构造方法
+     * @param name 姓名
+     * @param age 年龄
+     * @param studentId 学号
+     */
+    public Student(String name, Integer age, String studentId) {
+        this.name = name;
+        this.age = age;
+        this.studentId = studentId;
+    }
+
+    // ========== Getter 和 Setter 方法 ==========
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public String getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(String studentId) {
+        this.studentId = studentId;
+    }
+
+    /**
+     * 重写 toString 方法,方便打印对象信息
+     */
+    @Override
+    public String toString() {
+        return "Student{" +
+                "name='" + name + '\'' +
+                ", age=" + age +
+                ", studentId='" + studentId + '\'' +
+                '}';
+    }
+
+    /**
+     * 重写 equals 方法,基于学号判断两个学生是否相等
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+        Student student = (Student) obj;
+        return studentId != null && studentId.equals(student.studentId);
+    }
+
+    /**
+     * 重写 hashCode 方法,基于学号生成哈希值
+     */
+    @Override
+    public int hashCode() {
+        return studentId != null ? studentId.hashCode() : 0;
+    }
+}

+ 74 - 0
授课代码/c260729/src/exericse/exericse03/StudentManager.java

@@ -0,0 +1,74 @@
+package exericse.exericse03;
+
+import java.util.ArrayList;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title StudentManager
+ * @description
+ * @create 2026/7/29
+ */
+public class StudentManager {
+    public static void main(String[] args) {
+        ArrayList<Student> list = new ArrayList<>();
+        Student stu1 = new Student("张三", 20, "S001");
+        Student stu2 = new Student("李四", 21, "S002");
+        Student stu3 = new Student("王五", 19, "S003");
+        Student stu4 = new Student("赵六", 22, "S004");
+        list.add(stu1);
+        list.add(stu2);
+        list.add(stu3);
+        list.add(stu4);
+
+        // 假设已有练习 1 中的 list 列表(张三、李四、王五、赵六)
+
+        // 1. 计算所有学生的平均年龄(注意:age 是 Integer 类型,参与运算时会自动拆箱)
+        //    输出格式:"平均年龄:XX.X"
+        double sumAge=0;
+        for (int i = 0; i < list.size(); i++) {
+            Student s = list.get(i);
+            sumAge+=s.getAge();
+        }
+        System.out.println("平均年龄:"+(sumAge/list.size()));
+        
+        
+        // 2. 统计年龄 >= 20 岁的学生,输出他们的信息
+        //    输出格式:
+        //      年龄 >= 20 岁的学生:
+        //      学号:S001, 姓名:张三, 年龄:20
+        //      ...
+
+        for (int i = 0; i < list.size(); i++) {
+            Student s = list.get(i);
+            if (s.getAge()>=20){
+                System.out.println(s);
+            }
+        }
+        // 3. 找出年龄最大的学生,输出其信息
+        //    输出格式:"年龄最大的学生:学号:S002, 姓名:李四, 年龄:21"
+        Student maxStudent=list.get(0);
+        for (int i = 1; i < list.size(); i++) {
+            Student s = list.get(i);
+            if (s.getAge()>maxStudent.getAge()){
+                maxStudent=s;
+            }
+        }
+        System.out.println(maxStudent);
+
+        // 4. 按年龄从小到大输出所有学生信息(冒泡排序或选择排序,基于 ArrayList)
+        for (int i = 0; i < list.size()-1; i++) {
+            for (int j = 0; j < list.size()-1-i; j++) {
+                if (list.get(j).getAge()>list.get(j+1).getAge()){
+                    Student temp=list.get(j);
+                    list.set(j,list.get(j+1));  //在指定位置修改元素
+                    list.set(j+1,temp);
+                }
+            }
+        }
+        System.out.println(list);
+
+
+
+    }
+}

+ 309 - 0
课后作业/2026年7月29日-作业.md

@@ -0,0 +1,309 @@
+# 2026-07-29 JavaSE 课后作业
+
+> **课程内容**:包装类(Wrapper Classes)、集合框架入门(Collection Framework)、ArrayList 底层实现、泛型(Generics)
+>
+> **涉及前序知识**:数组、面向对象、构造方法、包装类、ArrayList 基本使用、泛型基础
+>
+> **提交方式**:在 `提交\姓名\` 目录下创建对应的 Java 源文件
+>
+> **截止时间**:下次上课前
+
+---
+
+## 📌 本次作业重要说明
+
+### 命名要求
+
+| 项目 | 要求 |
+|------|------|
+| **包名** | **必须按题目指定的包名创建**(全小写) |
+| **类名** | 自由命名,但需**见名知意** |
+| **测试类** | 建议命名为 `TestXxx` 或 `XxxTest` |
+| **文件名** | 必须与类名一致 |
+
+### 提交目录结构示例
+
+```
+提交\
+└─ 张三\
+    ├─ p1_arraylist_source\               ← 第 1~3 题的包
+    │   ├── ArrayListSourceAnalysis.java  ← 源码注释分析(含所有方法)
+    │   └── ArrayListSourceAnalysis.md    ← 思考题回答
+    └─ p2_myarraylist\                    ← 第 4 题(选做)
+        ├── MyArrayList.java              ← 手写简化版 ArrayList
+        └── MyArrayListTest.java          ← 测试类
+```
+
+### 作业说明
+
+本次作业的目的是让你**深入阅读 ArrayList 的源码**,理解其底层实现机制。请根据题目要求找到 JDK 中 `ArrayList.java` 的源码,对指定方法进行逐行注释分析。
+
+> 📌 **如何找到 ArrayList 源码?**
+> 1. 在 JDK 安装目录下找到 `src.zip`,解压后进入 `java.base/java/util/ArrayList.java`
+> 2. 或者使用 IDE(IntelliJ IDEA / Eclipse)直接打开 ArrayList 类,点击「Download Sources」
+> 3. 或者查阅 OpenJDK 在线源码:[ArrayList.java (JDK 21)](https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java)
+
+---
+
+## 第 1 题:ArrayList 构造方法源码注释分析 ⭐⭐⭐
+
+**包名**:`p1_arraylist_source`
+
+**知识点**:ArrayList 构造方法、默认容量、空数组与默认空数组的区别
+
+### 需求说明
+
+找到 `ArrayList.java` 中的以下三个构造方法,为**每一行代码**添加详细的中文注释(包括大括号和条件语句)。
+
+**要求**:
+- 注释必须包含该行代码的**作用说明**
+- 对于条件判断,需要说明什么条件下进入该分支
+- 对于方法调用,需要说明该方法的返回值或副作用
+- 对于你不认识的语法或 API,**必须上网查**并在注释中说明
+
+### 待注释的源码
+
+请复制以下三个构造方法到你的 Java 源文件中,并在每一行上方/后方添加注释:
+
+```java
+// ==================== 构造方法 1:指定初始容量 ====================
+public ArrayList(int initialCapacity) {
+    if (initialCapacity > 0) {
+        this.elementData = new Object[initialCapacity];
+    } else if (initialCapacity == 0) {
+        this.elementData = EMPTY_ELEMENTDATA;
+    } else {
+        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
+    }
+}
+```
+
+```java
+// ==================== 构造方法 2:无参构造 ====================
+public ArrayList() {
+    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+}
+```
+
+```java
+// ==================== 构造方法 3:传入集合 ====================
+public ArrayList(Collection<? extends E> c) {
+    Object[] a = c.toArray();
+    if ((size = a.length) != 0) {
+        if (c.getClass() == ArrayList.class) {
+            elementData = a;
+        } else {
+            elementData = Arrays.copyOf(a, size, Object[].class);
+        }
+    } else {
+        elementData = EMPTY_ELEMENTDATA;
+    }
+}
+```
+
+### 需要深入研究并回答的问题
+
+1. `EMPTY_ELEMENTDATA` 和 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA` 都是空数组,为什么要有两个?有什么区别?
+2. 无参构造中,为什么 `elementData` 不直接 `new Object[10]`,而是先指向一个空数组?
+3. 构造方法 3 中为什么要判断 `c.getClass() == ArrayList.class`?如果不判断直接使用 `Arrays.copyOf(a, size, Object[].class)` 会有什么问题?
+4. `Collection<? extends E> c` 中的 `? extends E` 是什么意思?有什么用?
+
+---
+
+## 第 2 题:ArrayList 扩容相关方法源码注释分析 ⭐⭐⭐⭐
+
+**包名**:`p1_arraylist_source`(与第 1 题同一个包)
+
+**知识点**:ArrayList 扩容机制、容量缩减、自动扩容、位运算
+
+### 需求说明
+
+为以下三个与容量 / 扩容相关的方法逐行添加中文注释。
+
+```java
+// ==================== trimToSize() - 缩减容量 ====================
+public void trimToSize() {
+    modCount++;
+    if (size < elementData.length) {
+        elementData = (size == 0)
+          ? EMPTY_ELEMENTDATA
+          : Arrays.copyOf(elementData, size);
+    }
+}
+```
+
+```java
+// ==================== ensureCapacity(int) - 确保容量 ====================
+public void ensureCapacity(int minCapacity) {
+    if (minCapacity > elementData.length
+        && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+             && minCapacity <= DEFAULT_CAPACITY)) {
+        modCount++;
+        grow(minCapacity);
+    }
+}
+```
+
+```java
+// ==================== grow(int) - 扩容核心方法 ====================
+private Object[] grow(int minCapacity) {
+    int oldCapacity = elementData.length;
+    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+        int newCapacity = ArraysSupport.newLength(oldCapacity,
+                minCapacity - oldCapacity,
+                oldCapacity >> 1);
+        return elementData = Arrays.copyOf(elementData, newCapacity);
+    } else {
+        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+    }
+}
+```
+
+### 需要深入研究并回答的问题
+
+1. **`modCount` 是什么?** 它在 ArrayList 中起什么作用?(提示:fail-fast 机制)
+2. **`ArraysSupport.newLength(int oldLength, int minGrowth, int prefGrowth)`** 这个方法是干什么的?三个参数分别是什么含义?它的返回值是如何计算的?
+3. **`oldCapacity >> 1`** 计算结果是多少?为什么扩容时选择"旧容量的一半"作为 preferred growth?
+4. **`ensureCapacity` 方法中的两个条件为什么要用 `&&` 连接?** 什么情况下即使 `minCapacity > elementData.length` 也不会立即扩容?
+5. **`trimToSize()` 中的三目运算符** `(size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size)` 分别对应什么场景?
+6. **`grow()` 方法中的 `if-else` 分支**:什么情况下进入 `if` 分支?什么情况下进入 `else` 分支?
+
+---
+
+## 第 3 题:ArrayList 增删改核心方法源码注释分析 ⭐⭐⭐⭐⭐
+
+**包名**:`p1_arraylist_source`(与第 1 题同一个包)
+
+**知识点**:ArrayList 添加元素、删除元素、清空集合、数组拷贝
+
+### 需求说明
+
+为以下四个与元素操作相关的方法逐行添加中文注释。
+
+```java
+// ==================== add(E, Object[], int) - 内部添加辅助方法 ====================
+private void add(E e, Object[] elementData, int s) {
+    if (s == elementData.length)
+        elementData = grow();
+    elementData[s] = e;
+    size = s + 1;
+}
+```
+
+```java
+// ==================== remove(int) - 按索引删除元素 ====================
+public E remove(int index) {
+    Objects.checkIndex(index, size);
+    final Object[] es = elementData;
+
+    @SuppressWarnings("unchecked")
+    E oldValue = (E) es[index];
+    fastRemove(es, index);
+
+    return oldValue;
+}
+```
+
+```java
+// ==================== fastRemove(Object[], int) - 快速删除(不检查边界) ====================
+private void fastRemove(Object[] es, int i) {
+    modCount++;
+    final int newSize;
+    if ((newSize = size - 1) > i)
+        System.arraycopy(es, i + 1, es, i, newSize - i);
+    es[size = newSize] = null;
+}
+```
+
+```java
+// ==================== clear() - 清空集合 ====================
+public void clear() {
+    modCount++;
+    final Object[] es = elementData;
+    for (int to = size, i = size = 0; i < to; i++)
+        es[i] = null;
+}
+```
+
+### 需要深入研究并回答的问题
+
+1. **`add(E e)` 的返回类型是 `boolean`,但始终返回 `true`**,为什么还要设计成有返回值?
+2. **`add(E e, Object[] elementData, int s)` 为什么被设计成 `private` 方法?** 为什么要把参数 `elementData` 和 `s` 传进来,而不是直接在方法内访问成员变量?
+3. **`Objects.checkIndex(index, size)`** 是做什么的?如果 index 越界会怎样?
+4. **`@SuppressWarnings("unchecked")`** 是什么作用?为什么这里需要加这个注解?
+5. **`fastRemove` 方法中的 `System.arraycopy`** 是做什么的?参数 `es`, `i + 1`, `es`, `i`, `newSize - i` 分别代表什么?
+6. **`fastRemove` 删除元素后,为什么要将最后一个位置设为 `null`?**(即 `es[size = newSize] = null;`)
+7. **`clear()` 方法的 for 循环中**,为什么 `i < to` 的条件不直接写 `i < size`?为什么要先把 `size` 赋值给 `to`,然后设置 `size = 0`?
+8. **`clear()` 方法遍历置 null 和直接 `elementData = EMPTY_ELEMENTDATA` 有什么区别?** 哪种方式更好?为什么 JDK 选择了前者?
+
+---
+
+## 第 4 题:手写简化版 MyArrayList(选做 / 加分题)⭐⭐⭐⭐⭐
+
+**包名**:`p2_myarraylist`
+
+**知识点**:综合运用泛型、数组操作、扩容机制
+
+### 需求说明
+
+基于你对 ArrayList 源码的理解,手写一个简化版的 `MyArrayList<E>` 类,实现以下方法:
+
+| 方法 | 说明 |
+|------|------|
+| `MyArrayList()` | 无参构造,默认容量 10 |
+| `MyArrayList(int initialCapacity)` | 指定初始容量 |
+| `boolean add(E e)` | 添加元素(自动扩容) |
+| `E get(int index)` | 获取元素(检查越界) |
+| `E set(int index, E element)` | 修改元素 |
+| `E remove(int index)` | 删除元素(返回被删除的元素) |
+| `int size()` | 返回元素个数 |
+| `boolean isEmpty()` | 判断是否为空 |
+| `void clear()` | 清空所有元素 |
+| `String toString()` | 返回格式如 `[元素1, 元素2, ...]` |
+
+**要求**:
+1. 必须使用**泛型**(`MyArrayList<E>`)
+2. 底层使用 `Object[]` 存储数据
+3. 实现自动扩容机制(参考 JDK 的 1.5 倍扩容)
+4. 所有方法必须添加详细中文注释
+5. 编写测试类 `MyArrayListTest` 测试所有方法
+
+**测试数据示例**:
+
+```java
+MyArrayList<String> list = new MyArrayList<>();
+list.add("Java");
+list.add("Python");
+list.add("C++");
+System.out.println(list);           // [Java, Python, C++]
+System.out.println(list.size());    // 3
+System.out.println(list.get(1));    // Python
+list.set(1, "JavaScript");
+list.remove(0);
+System.out.println(list);           // [JavaScript, C++]
+System.out.println(list.isEmpty()); // false
+list.clear();
+System.out.println(list.isEmpty()); // true
+```
+
+---
+
+# 评分标准
+
+| 题号 | 分值 | 评分维度 |
+|------|------|---------|
+| 第 1 题 | 25 分 | 注释是否覆盖每一行、注释是否准确、思考题是否回答正确 |
+| 第 2 题 | 30 分 | 注释深度(是否有查阅资料)、扩容机制理解是否正确、思考题回答质量 |
+| 第 3 题 | 30 分 | 增删改方法注释是否完整、System.arraycopy 理解是否正确、思考题回答质量 |
+| 第 4 题 | 15 分(加分) | 功能完整性、代码质量、测试覆盖度 |
+
+---
+
+# 参考资料
+
+- JDK 源码 `ArrayList.java`(位于 JDK 安装目录的 `src.zip` 中)
+- [OpenJDK ArrayList.java (JDK 21)](https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java)
+- [Java ArrayList 官方文档](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ArrayList.html)
+- [System.arraycopy 方法详解](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#arraycopy(java.lang.Object,int,java.lang.Object,int,int))
+- [Java 泛型通配符详解](https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html)
+