|
@@ -270,7 +270,10 @@ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
|
|
|
transient Object[] elementData;
|
|
transient Object[] elementData;
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
**扩容流程:**
|
|
**扩容流程:**
|
|
|
|
|
+
|
|
|
1. 使用无参构造时,`elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA`(容量为 0 的空数组)
|
|
1. 使用无参构造时,`elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA`(容量为 0 的空数组)
|
|
|
2. 第一次添加元素时,立即将容量**扩容到 `DEFAULT_CAPACITY`(10)**
|
|
2. 第一次添加元素时,立即将容量**扩容到 `DEFAULT_CAPACITY`(10)**
|
|
|
3. 后续元素超出容量时,自动按 **1.5 倍** 扩容(`oldCapacity + (oldCapacity >> 1)`)
|
|
3. 后续元素超出容量时,自动按 **1.5 倍** 扩容(`oldCapacity + (oldCapacity >> 1)`)
|
|
@@ -312,6 +315,76 @@ System.out.println(list03.size()); // 0
|
|
|
|
|
|
|
|
> 💡 **注意**:`ArrayList` 的 `size` 是元素个数,`elementData.length` 才是数组的物理容量(`capacity`),二者不一定相等。`size <= capacity` 恒成立。
|
|
> 💡 **注意**:`ArrayList` 的 `size` 是元素个数,`elementData.length` 才是数组的物理容量(`capacity`),二者不一定相等。`size <= capacity` 恒成立。
|
|
|
|
|
|
|
|
|
|
+#### 2.4.5 ArrayList 扩容相关方法
|
|
|
|
|
+
|
|
|
|
|
+##### `trimToSize()` — 缩减容量
|
|
|
|
|
+
|
|
|
|
|
+将底层数组的容量缩减为当前有效元素的个数,以节省内存空间。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 底层实现逻辑
|
|
|
|
|
+elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- 如果 `size == 0`,将 `elementData` 设置为 `EMPTY_ELEMENTDATA`(空数组)
|
|
|
|
|
+- 否则,通过 `Arrays.copyOf()` 复制到新的数组,新数组长度为 `size`
|
|
|
|
|
+
|
|
|
|
|
+##### `ensureCapacity(int minCapacity)` — 确保容量(手动扩容)
|
|
|
|
|
+
|
|
|
|
|
+在向数组添加元素之前,都会先检查添加后的元素个数是否会超过当前数组长度。如果超过,就会扩容。数组扩容通过 `ensureCapacity()` 来实现。
|
|
|
|
|
+
|
|
|
|
|
+这个方法用 `public` 修饰,开发者可以 **主动调用** 来手动扩容——当需要添加大量元素之前,提前扩容能提升性能。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 手动扩容到期望的最小容量
|
|
|
|
|
+ArrayList list = new ArrayList<>();
|
|
|
|
|
+list.ensureCapacity(100); // 提前扩容到 100,避免多次自动扩容
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**扩容触发条件**(两个条件同时满足才会扩容):
|
|
|
|
|
+1. `minCapacity > elementData.length` —— 期望的最小容量大于当前数组长度
|
|
|
|
|
+2. `!(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA && minCapacity <= DEFAULT_CAPACITY)` —— 既不是默认空数组,也不是期望容量 ≤ 默认容量
|
|
|
|
|
+
|
|
|
|
|
+> 通俗解释:如果是刚 new 出来的空 `ArrayList`(`elementData` 为空数组),且期望容量没超过 10,不会立即扩容,等到第一次 `add` 时再扩容到 10。
|
|
|
|
|
+
|
|
|
|
|
+##### `grow(int minCapacity)` — 扩容核心方法
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+Object[] grow(int minCapacity) {
|
|
|
|
|
+ // oldCapacity: 旧的容量
|
|
|
|
|
+ // minCapacity - oldCapacity: 最小需要增加的容量
|
|
|
|
|
+ // oldCapacity >> 1: 最好增加的容量(旧容量除以 2)
|
|
|
|
|
+ return Arrays.copyOf(elementData,
|
|
|
|
|
+ ArraysSupport.newLength(oldCapacity,
|
|
|
|
|
+ minCapacity - oldCapacity,
|
|
|
|
|
+ oldCapacity >> 1));
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**扩容计算公式:**
|
|
|
|
|
+
|
|
|
|
|
+| 变量 | 说明 | 示例(旧容量 = 10) |
|
|
|
|
|
+|------|------|-------------------|
|
|
|
|
|
+| `oldCapacity` | 旧容量 | 10 |
|
|
|
|
|
+| `minCapacity - oldCapacity` | 最小需要增加的容量 | 最少加 1 |
|
|
|
|
|
+| `oldCapacity >> 1` | 最好增加的容量 = 旧容量 ÷ 2 | `10 >> 1 = 5` |
|
|
|
|
|
+| **新容量** | `oldCapacity + oldCapacity >> 1` = **旧容量的 1.5 倍** | `10 + 5 = 15` |
|
|
|
|
|
+
|
|
|
|
|
+> ✅ **结论**:`ArrayList` 每次扩容时,新容量 = 旧容量 × **1.5 倍**(即 `oldCapacity + oldCapacity / 2`)
|
|
|
|
|
+
|
|
|
|
|
+##### `main` 方法中的位移运算演示
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+System.out.println(20 << 1); // 20 × 2 = 40
|
|
|
|
|
+System.out.println(20 >> 1); // 20 ÷ 2 = 10
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- `<<`:左移,相当于乘以 2
|
|
|
|
|
+- `>>`:右移,相当于除以 2
|
|
|
|
|
+- `oldCapacity >> 1` 即为 `oldCapacity / 2`
|
|
|
|
|
+
|
|
|
|
|
+> 📄 来源:`collection/Demo02.java`
|
|
|
|
|
+
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
## 三、随堂练习要点
|
|
## 三、随堂练习要点
|
|
@@ -326,6 +399,8 @@ System.out.println(list03.size()); // 0
|
|
|
| 集合框架概述 | 集合 vs 数组对比,体系结构 | `collection/Demo01.java` |
|
|
| 集合框架概述 | 集合 vs 数组对比,体系结构 | `collection/Demo01.java` |
|
|
|
| 线性表数据结构 | 栈/队列/数组/链表 特点 | `collection/Demo02.java` |
|
|
| 线性表数据结构 | 栈/队列/数组/链表 特点 | `collection/Demo02.java` |
|
|
|
| **ArrayList 底层原理** | **可变数组、默认容量10、扩容机制、三个构造方法、size vs capacity** | `collection/Demo02.java` |
|
|
| **ArrayList 底层原理** | **可变数组、默认容量10、扩容机制、三个构造方法、size vs capacity** | `collection/Demo02.java` |
|
|
|
|
|
+| **ArrayList 扩容方法** | **trimToSize 缩减容量、ensureCapacity 手动扩容、grow 1.5倍扩容公式** | `collection/Demo02.java` |
|
|
|
|
|
+| **位运算符** | **`>>` 右移(除2)、`<<` 左移(乘2)在扩容中的使用** | `collection/Demo02.java` |
|
|
|
|
|
|
|
|
### 包装类重点
|
|
### 包装类重点
|
|
|
|
|
|
|
@@ -348,6 +423,10 @@ System.out.println(list03.size()); // 0
|
|
|
- 三个构造方法的区别
|
|
- 三个构造方法的区别
|
|
|
- `size` 与 `capacity` 的区别:`size` 是元素个数,`capacity` 是数组物理长度
|
|
- `size` 与 `capacity` 的区别:`size` 是元素个数,`capacity` 是数组物理长度
|
|
|
- 允许存储 `null`,非线程安全
|
|
- 允许存储 `null`,非线程安全
|
|
|
|
|
+ - `trimToSize()`:缩减容量到有效元素个数
|
|
|
|
|
+ - `ensureCapacity()`:手动提前扩容优化性能
|
|
|
|
|
+ - `grow()`:每次扩容为旧容量的 **1.5 倍**(`oldCapacity + oldCapacity >> 1`)
|
|
|
|
|
+ - `>>` 右移位运算等价于除以 2
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|