Browse Source

docs: 更新20260729课堂笔记,同步代码变更——新增ArrayList扩容方法详解:trimToSize缩减容量、ensureCapacity手动扩容、grow 1.5倍扩容公式(oldCapacity >> 1)、位移运算符>> <<在扩容中的应用

WanJL 9 hours ago
parent
commit
71b250f6f7

+ 2 - 2
README.md

@@ -34,7 +34,7 @@
 | 2026-07-24 | [多态 + 多态中的转型 + 内部类:多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof类型判断、多态的好处(开闭原则)、抽象类与多态结合(Animal→Dog;Person→Student/Teacher综合案例)、Usb接口多态案例(NoteBook接入鼠标/猫)、内部类概念与使用场景、成员内部类、私有内部类设计原则](./课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md) |
 | 2026-07-27 | [匿名内部类 + Java API 与 String 类 + Math + 日期时间:内部类三大分类、匿名内部类四种使用方式、综合案例(USB/餐具/学生排序/计算器/动物叫声);API概念、String内存模型/不可变性/可变字符串StringBuffer&StringBuilder/文本块TextBlock JDK15+/String常用方法大全;Math数学类常用方法;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter)](./课堂笔记/01-JavaSE基础-笔记/20260727-笔记.md) |
 | 2026-07-28 | [Java 异常处理:异常概念、Throwable 体系结构、编译期/运行时异常、JVM 默认处理、try-catch 多 catch 块、异常常用方法、finally 资源关闭、throws 抛出、鲁棒性;自定义异常、throw/throws;Object 类:equals/hashCode/toString 重写、Objects 工具类;课后作业回顾 — String 分割去重/统计、LocalDate 天数差计算](./课堂笔记/01-JavaSE基础-笔记/20260728-笔记.md) |
-| 2026-07-29 | [包装类(Wrapper Classes)+ 集合框架入门(Collection Framework):基本类型与包装类对应关系、装箱与拆箱、自动装箱/拆箱、包装类优势、缓存机制、Bean 类应用;集合 vs 数组、体系结构(Collection/List/Set/Map)、线性表数据结构(栈/队列/数组/链表)、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity)](./课堂笔记/01-JavaSE基础-笔记/20260729-笔记.md) |
+| 2026-07-29 | [包装类(Wrapper Classes)+ 集合框架入门(Collection Framework):基本类型与包装类对应关系、装箱与拆箱、自动装箱/拆箱、包装类优势、缓存机制、Bean 类应用;集合 vs 数组、体系结构(Collection/List/Set/Map)、线性表数据结构(栈/队列/数组/链表)、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity)、ArrayList 扩容方法(trimToSize / ensureCapacity / grow 1.5倍扩容公式)、位运算符 >> << 在扩容中的应用](./课堂笔记/01-JavaSE基础-笔记/20260729-笔记.md) |
 
 ## 二、授课代码
 
@@ -55,7 +55,7 @@
 | 2026-07-24 | 多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof运算符、多态的好处(开闭原则)、抽象类+多态(Animal→Dog、Person→Student/Teacher→Test03.runPerson())、接口多态(Usb→Mouse/Cat→NoteBook)、内部类概念与使用场景 | `c260724` | [`course/polymorphism`](./授课代码/c260724/src/course/polymorphism/) / [`course/internalClass`](./授课代码/c260724/src/course/internalClass/) |
 | 2026-07-27 | 匿名内部类:内部类三大分类概念回顾、匿名内部类语法格式与本质、三种使用方式(多态接收变量赋值 / 方法参数传递 / 匿名对象直接调用)、配套接口/抽象类定义(Usb / Tableware / Compute / Sort)、综合案例(NoteBook USB设备 / Person 餐具 / 学生排序比较 / Calculator计算器 / Animal动物叫声);Java API:API概念、String类(内存模型/构造方法/==与equals/常用方法大全/不可变性)、可变字符串(StringBuffer/SringBuilder)、文本块TextBlock JDK15+;Math数学类;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter) | `c260727` | [`course/anonymousInternalClass`](./授课代码/c260727/src/course/anonymousInternalClass/) / [`course/api`](./授课代码/c260727/src/course/api/) / [`course/api/string`](./授课代码/c260727/src/course/api/string/) / [`course/api/math`](./授课代码/c260727/src/course/api/math/) / [`course/api/date`](./授课代码/c260727/src/course/api/date/) / [`exercise/exercise01`](./授课代码/c260727/src/exercise/exercise01/) / [`exercise/exercise06`](./授课代码/c260727/src/exercise/exercise06/) / [`exercise/exercise07`](./授课代码/c260727/src/exercise/exercise07/) / [`homework_0724/p2_myarray`](./授课代码/c260727/src/homework_0724/p2_myarray/) |
 | 2026-07-28 | 异常处理(Demo01~Demo04 + Person+AgeOutOfBoundsException) + Object类(Demo01+Person 重写equals/hashCode/toString) + 课后作业回顾(String工具 + 日期数学) + Exercises | `c260728` | [`course/api`](./授课代码/c260728/src/course/api/) / [`course/exception`](./授课代码/c260728/src/course/exception/) / [`homework260727`](./授课代码/c260728/src/homework260727/) / [`exercise/exercise01`](./授课代码/c260728/src/exercise/exercise01/) / [`exercise/exercise07`](./授课代码/c260728/src/exercise/exercise07/) |
-| 2026-07-29 | 包装类(Wrapper Classes):装箱与拆箱、自动装箱/拆箱、包装类在 Bean 类中的应用(Person + Demo01) + 集合框架入门(Collection Framework):集合 vs 数组、体系结构(Collection/List/Set/Map)、线性表数据结构(栈/队列/数组/链表)、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity) | `c260729` | [`course/packaging`](./授课代码/c260729/src/course/packaging/) / [`course/collection`](./授课代码/c260729/src/course/collection/) |
+| 2026-07-29 | 包装类(Wrapper Classes):装箱与拆箱、自动装箱/拆箱、包装类在 Bean 类中的应用(Person + Demo01) + 集合框架入门(Collection Framework):集合 vs 数组、体系结构(Collection/List/Set/Map)、线性表数据结构(栈/队列/数组/链表)、ArrayList 底层实现详解(默认容量10、扩容机制、三个构造方法、size vs capacity、trimToSize/ensureCapacity/grow 1.5倍扩容公式、位移运算符 >> 与 <<) | `c260729` | [`course/packaging`](./授课代码/c260729/src/course/packaging/) / [`course/collection`](./授课代码/c260729/src/course/collection/) |
 
 ## 三、课堂练习题
 

+ 36 - 0
授课代码/c260729/src/course/collection/Demo02.java

@@ -67,6 +67,37 @@ public class Demo02 {
                     ArrayList(Collection<? extends E> c)    创建一个ArrayList集合对象,传入的是Collection接口的实现类对象。
                     其实就是把另一个集合对象的元素加入到新建的ArrayList结合对象中
 
+                trimToSize():缩减容量为集合有效元素个数
+                        elementData = (size == 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);
+                        elementData =EMPTY_ELEMENTDATA
+                        elementData =Arrays.copyOf(elementData, size);
+
+                ensureCapacity(int minCapacity) 确保容量-自动扩容的前提
+                    每当向数组elementData添加元素的时候,都要去先检查一遍添加后的元素个数是否会超过当前的数组的长度。
+                    如果超过,就要扩容。数组扩容是通过ensureCapacity()来实现的,确保容量足够。
+                    在实际添加大量元素之前,我们可以使用ensureCapacity()来手动增加ArrayList的容量。
+                    这个方法是使用public修饰的,就是我们如果想要添加大量元素,就可以调用这个方法,手动扩容。
+                    直接扩容到期望最小容量minCapacity
+                        只有满足下面两个条件,才会进行扩容
+                        minCapacity > elementData.length    期望的最小容量要大于当前维护的数组长度
+                        !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA && minCapacity <= DEFAULT_CAPACITY)
+                        elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA  如果是空数组
+                        minCapacity <= DEFAULT_CAPACITY 期望的最小容量,如果小于等于默认容量
+                        两个条件同时满足后取反。也就是,elementData既不能是空数组,也不能是期望的最小容量小于等于默认容量。才会进行扩容
+
+                Object[] grow(int minCapacity)  扩容
+
+                oldCapacity 旧的容量
+                minCapacity - oldCapacity 最小要增加的容量
+                oldCapacity >> 1    最好增加的容量 就是  旧的容量除以2
+                ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, oldCapacity >> 1 );
+                比如 旧容量  10
+                最小      10
+                最好增加的容量     10/2 5
+                10+5  =15 刚好是原来的1.5倍
+
+                ArrayList每次扩容的时候,都是原来容量的1.5倍。
+
      */
     public static void main(String[] args) {
         ArrayList list01=new ArrayList<>(); //创建一个默认容量为10集合
@@ -76,6 +107,11 @@ public class Demo02 {
         System.out.println(list01.size());
         System.out.println(list02.size());
         System.out.println(list03.size());
+
+
+        System.out.println(20<<1);
+        System.out.println(20>>1);
+
     }
 
 

+ 79 - 0
课堂笔记/01-JavaSE基础-笔记/20260729-笔记.md

@@ -270,7 +270,10 @@ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 transient Object[] elementData;
 ```
 
+![image-20260729111736848](https://gitee.com/lk506667090/PicGo_img/raw/master/images/202607291117007.png)
+
 **扩容流程:**
+
 1. 使用无参构造时,`elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA`(容量为 0 的空数组)
 2. 第一次添加元素时,立即将容量**扩容到 `DEFAULT_CAPACITY`(10)**
 3. 后续元素超出容量时,自动按 **1.5 倍** 扩容(`oldCapacity + (oldCapacity >> 1)`)
@@ -312,6 +315,76 @@ System.out.println(list03.size()); // 0
 
 > 💡 **注意**:`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` |
 | 线性表数据结构 | 栈/队列/数组/链表 特点 | `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` 是数组物理长度
    - 允许存储 `null`,非线程安全
+   - `trimToSize()`:缩减容量到有效元素个数
+   - `ensureCapacity()`:手动提前扩容优化性能
+   - `grow()`:每次扩容为旧容量的 **1.5 倍**(`oldCapacity + oldCapacity >> 1`)
+   - `>>` 右移位运算等价于除以 2
 
 ---