## 第 2 题:综合大挑战——MyArray 可变数组(动态数组底层实现)⭐⭐⭐⭐⭐ **包名**:`p2_myarray` **知识点**:构造方法、数组扩容、方法封装、增删改查、元素前移、冒泡排序 > 模仿 `ArrayList` 的底层原理,自己动手实现一个可变数组(动态数组)。 ### 需求背景 Java 中普通数组 `int[]` 一旦创建,长度就固定了,无法动态增减元素。请自行实现一个 **可变整型数组** `MyArray` 类,内部使用 `int[]` 存储数据,对外提供便捷的增删改查方法。 ### 实体类说明:MyArray **私有属性**: | 属性 | 类型 | 说明 | | ------ | ------- | ------------------------------------ | | `data` | `int[]` | 内部真正存储数据的数组 | | `size` | `int` | 当前已存储的元素个数(不是数组长度) | **构造方法**: | 构造方法 | 说明 | | ----------------------- | --------------------------------------------------- | | `MyArray()` | 无参构造,默认初始化容量为 **10** 的数组 | | `MyArray(int capacity)` | 指定初始容量,如果传入的容量 ≤ 0,则使用默认容量 10 | ### 核心方法列表(共 11 个) #### 1. 添加元素 — `add(int element)` - 如果内部数组已满,自动扩容为当前容量的 **1.5 倍**(`data.length * 3 / 2`) - 扩容步骤:创建新数组 → 复制旧元素 → 替换 `data` 引用 - 将新元素存入 `data[size]`,`size` 自增 1 #### 2. 获取元素 — `get(int index)` - 下标越界时输出 `"下标越界,无法获取元素"`,返回 `-1` - 否则返回 `data[index]` #### 3. 修改元素 — `set(int index, int element)` - 下标越界时输出 `"下标越界,无法修改元素"` - 否则将 `data[index]` 赋值为 `element` #### 4. 获取长度 — `size()` - 返回 `size` 属性值 #### 5. 获取容量 — `capacity()` - 返回 `data.length` #### 6. 判断是否为空 — `isEmpty()` - `size == 0` 返回 `true`,否则 `false` #### 7. 查找元素下标 — `indexOf(int element)` - 从前向后遍历,找到第一个匹配的元素下标,没找到返回 `-1` #### 8. 判断是否包含 — `contains(int element)` - 调用 `indexOf()`,根据返回值是否为 `-1` 判断 #### 9. 删除指定位置的元素 — `remove(int index)` - 下标越界时输出 `"下标越界,无法删除元素"`,返回 `-1` - 否则:保存被删除的值 → 后面元素前移一位 → `size--` → 返回被删除的值 #### 10. 删除指定值的元素 — `removeByValue(int element)` - 调用 `indexOf()` 获取下标 - 下标为 `-1` 则输出 `"未找到该元素,删除失败"`,返回 `false` - 否则调用 `remove(index)`,返回 `true` #### 11. 数组排序 — `sort(boolean ascending)` - `true` 升序,`false` 降序 - 使用**冒泡排序**,只对 `data[0] ~ data[size-1]` 排序 ### 辅助方法 #### `print()` - 遍历输出格式:`[元素1, 元素2, 元素3, ...]` - 空数组输出 `[]` #### `clear()` - 将 `size` 置为 `0`(内部数组保留) ### 测试类要求 **`MyArrayTest` 类**: ```java public class MyArrayTest { public static void main(String[] args) { // 1. 创建一个初始容量为 3 的 MyArray 对象 MyArray arr = new MyArray(3); // 2. 输出初始状态 System.out.println("初始容量:" + arr.capacity()); // 3 System.out.println("是否为空:" + arr.isEmpty()); // true // 3. 添加 5 个元素(触发自动扩容) for (int i = 1; i <= 5; i++) { arr.add(i * 10); // 添加 10, 20, 30, 40, 50 } arr.print(); // [10, 20, 30, 40, 50] // 4. 测试各个方法 System.out.println("元素个数:" + arr.size()); // 5 System.out.println("当前容量:" + arr.capacity()); // 6 System.out.println("下标2的元素:" + arr.get(2)); // 30 System.out.println("是否包含40:" + arr.contains(40)); // true System.out.println("50的下标:" + arr.indexOf(50)); // 4 // 5. 测试修改 arr.set(1, 99); arr.print(); // [10, 99, 30, 40, 50] // 6. 测试删除 int deleted = arr.remove(2); System.out.println("被删除的元素:" + deleted); // 30 arr.print(); // [10, 99, 40, 50] // 7. 测试按值删除 arr.removeByValue(10); arr.print(); // [99, 40, 50] // 8. 测试升序排序 arr.sort(true); arr.print(); // [40, 50, 99] // 9. 测试降序排序 arr.sort(false); arr.print(); // [99, 50, 40] // 10. 测试越界访问 System.out.println(arr.get(10)); // 下标越界,无法获取元素 → -1 arr.remove(10); // 下标越界,无法删除元素 → -1 } } ``` ### 思考题 1. 如果 `add()` 方法中每次只扩容 1 个容量(`data.length + 1`),当添加大量元素时性能会怎样?为什么选择 1.5 倍? 2. `remove()` 方法中为什么要将后面的元素向前移动?如果删除的是最后一个元素还需要移动吗? 3. 如何将 `MyArray` 改造为支持任意类型的 `MyArrayList`?