# 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 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 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` 类,实现以下方法: | 方法 | 说明 | |------|------| | `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`) 2. 底层使用 `Object[]` 存储数据 3. 实现自动扩容机制(参考 JDK 的 1.5 倍扩容) 4. 所有方法必须添加详细中文注释 5. 编写测试类 `MyArrayListTest` 测试所有方法 **测试数据示例**: ```java MyArrayList 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)