Java学习20
上午 3h LinkedList 深度学习
1.1 LinkedList 底层结构与核心特点(0.6h)
底层核心
ArrayList底层:动态可变数组LinkedList底层:双向双向链表- 链表没有固定连续内存空间,不存在数组扩容、元素移位问题
核心特性
- 元素以节点形式存储,每个节点:存数据 + 上一个节点地址 + 下一个节点地址
- 无初始容量、无扩容机制,元素按需创建节点
- 首尾增删极快,只需要修改节点引用地址
- 根据索引查询、中间位置操作很慢,必须从头 / 尾逐个遍历查找
结构对比表(必背)
表格
| 集合 | 底层结构 | 查询 / 遍历 | 中间增删 | 首尾增删 |
|---|---|---|---|---|
| ArrayList | 动态数组 | 极快 | 慢 | 慢 |
| LinkedList | 双向链表 | 慢 | 慢 | 极快 |
核心结论
- 查多改少、展示列表、遍历数据 → 优先
ArrayList - 频繁头尾增删、做队列 / 栈结构 → 优先
LinkedList
1.2 LinkedList 独有首尾专属方法(1.2h)
继承 List 全部通用方法,额外提供首尾操作专属 API,适合链表结构。
表格
| 专属方法 | 作用 |
|---|---|
addFirst(E e) | 集合头部添加元素 |
addLast(E e) | 集合尾部添加元素 |
getFirst() | 获取第一个元素 |
getLast() | 获取最后一个元素 |
removeFirst() | 删除并返回首元素 |
removeLast() | 删除并返回尾元素 |
完整案例代码 + 逐行解析
java
运行
import java.util.LinkedList; public class LinkedListFirstLastDemo { public static void main(String[] args) { // 1. 创建LinkedList集合,泛型约束存储字符串 LinkedList<String> link = new LinkedList<>(); // 2. 尾部添加(等价普通add) link.addLast("张三"); link.addLast("李四"); // 3. 头部添加 link.addFirst("赵六"); System.out.println("首尾添加后:" + link); // 4. 获取首尾元素 String first = link.getFirst(); String last = link.getLast(); System.out.println("首元素:" + first); System.out.println("尾元素:" + last); // 5. 删除首尾元素 link.removeFirst(); link.removeLast(); System.out.println("删除首尾后:" + link); } }逐行解释
LinkedList<String> link = new LinkedList<>();创建双向链表集合,仅允许存储字符串;addFirst / addLast:利用链表引用特性,直接绑定头尾节点,效率极高;getFirst/getLast:直接获取头尾节点,无需遍历;removeFirst/removeLast:断开头尾节点引用,自动回收节点,无元素移位。
1.3 LinkedList 通用 List 方法(0.6h)
核心说明
LinkedList实现了List接口:
- 满足 List 三大特性:有序、可重复、带索引
- 完全复用:
add()、remove(索引)、set()、get()、size()、clear() - 支持 Day14 全部三种遍历:普通 for、增强 for、迭代器
通用索引操作代码示例
java
运行
import java.util.LinkedList; public class LinkedListCommonDemo { public static void main(String[] args) { LinkedList<Integer> list = new LinkedList<>(); list.add(10); list.add(20); list.add(30); // 根据索引获取 System.out.println("索引1元素:" + list.get(1)); // 根据索引修改 list.set(0, 99); // 根据索引删除 list.remove(2); System.out.println("最终集合:" + list); } }关键短板
LinkedList调用get(索引)时,底层会从头节点循环遍历找位置,数据量大时性能很差。
1.4 适用场景总结(0.6h)
- 适合场景
- 频繁在头部、尾部插入 / 删除数据
- 实现简单队列、栈、消息排队结构
- 数据量波动大,不想考虑数组扩容
- 不适合场景
- 大量根据索引查询、分页展示、列表遍历
- 项目常规业务数据存储(90% 场景用 ArrayList)
下午 2.5h 泛型 + 迭代器完整用法 + 增强 for 底层
2.1 泛型 核心概念与作用(1h)
1)为什么需要泛型
- 不写泛型:集合默认存储
Object类型,什么类型都能存 - 取出元素必须强制类型转换,代码繁琐
- 极易发生
类型转换异常 ClassCastException
2)泛型核心三大作用
- 类型约束:规定集合只能存指定类型
- 编译校验:编译阶段就报错,杜绝存错数据
- 省去强转:取出元素直接是目标类型,简化代码
3)基础语法
java
运行
集合<存储类型> 变量名 = new 实现类<>();4)无泛型 / 有泛型 对比完整代码
① 无泛型(弊端演示)
java
运行
import java.util.ArrayList; public class NoGenericDemo { public static void main(String[] args) { // 无泛型,默认Object ArrayList list = new ArrayList(); list.add("Java"); list.add(123); // 乱存,编译不报错 // 取出必须强转 String s = (String) list.get(0); // String s2 = (String) list.get(1); // 运行报错:类型转换异常 } }② 有泛型(安全规范写法)
java
运行
import java.util.ArrayList; public class GenericDemo { public static void main(String[] args) { // 泛型约束:只能存String ArrayList<String> list = new ArrayList<>(); list.add("Python"); // list.add(666); 编译直接报错,防止错误数据存入 // 无需强转,直接取值 String str = list.get(0); System.out.println(str); } }5)泛型书写规则
- 接口 / 类后加尖括号:
List<E>、ArrayList<E>、LinkedList<E> - 泛型只能写引用类型:String、Integer、自定义实体类
- 基本类型不能写:
int不行,要用包装类Integer
2.2 迭代器 Iterator 完整用法(0.8h)
1)迭代器定位
- 所有
Collection单列集合通用遍历方式 - 不依赖索引,适配数组、链表所有集合
- 遍历集合时,安全删除元素唯一方案
2)三大核心方法
iterator():调用集合方法,获取迭代器对象hasNext():判断是否存在下一个未遍历元素,返回布尔值next():获取下一个元素,指针后移
3)标准默写模板 + 完整代码
java
运行
import java.util.ArrayList; import java.util.Iterator; public class IteratorFullDemo { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("高数"); list.add("英语"); list.add("Java"); // 1. 获取迭代器对象 Iterator<String> it = list.iterator(); // 2. 循环判断是否有下一个元素 while (it.hasNext()){ // 3. 取出元素 String course = it.next(); System.out.println(course); } } }逐行解析
list.iterator():集合底层生成迭代器,记录遍历起始位置;it.hasNext():内部判断指针是否到达集合末尾;it.next():取出当前元素,同时迭代器指针向后移动一位。
2.3 增强 for 循环底层原理(0.7h)
1)底层本质
增强 for 循环底层完全就是迭代器是迭代器的简化写法,简化代码、去掉迭代器对象书写
2)标准格式
java
运行
for(元素类型 变量名 : 集合/数组){ 循环操作 }3)代码演示
java
运行
for (String s : list) { System.out.println(s); }4)致命限制(高频考点)
遍历过程中不能添加、删除集合元素会抛出:ConcurrentModificationException 并发修改异常
- 原因:增强 for 底层迭代器不允许遍历期间修改集合长度
- 解决方案:需要增删,必须用原生迭代器
晚上 1.5h 综合练习 + List 全复盘
3.1 今日必做实战练习(可直接抄写运行)
练习 1:LinkedList 全套首尾操作
java
运行
import java.util.LinkedList; public class LinkPractice { public static void main(String[] args) { LinkedList<String> link = new LinkedList<>(); link.addFirst("元素1"); link.addLast("元素2"); link.addFirst("元素0"); System.out.println("首元素:" + link.getFirst()); System.out.println("尾元素:" + link.getLast()); link.removeFirst(); link.removeLast(); System.out.println(link); } }练习 2:三种方式遍历 LinkedList
java
运行
import java.util.Iterator; import java.util.LinkedList; public class LinkTraverse { public static void main(String[] args) { LinkedList<Integer> numList = new LinkedList<>(); numList.add(1); numList.add(2); numList.add(3); // 1.普通for for (int i = 0; i < numList.size(); i++) { System.out.print(numList.get(i)+" "); } System.out.println("\n--------"); // 2.增强for for (Integer n : numList) { System.out.print(n+" "); } System.out.println("\n--------"); // 3.迭代器 Iterator<Integer> it = numList.iterator(); while (it.hasNext()){ System.out.print(it.next()+" "); } } }练习 3:泛型安全对比
自行对比无泛型随意存、有泛型限制类型的编译区别,加深理解。
3.2 高频面试简答(直接背诵)
- ArrayList 和 LinkedList 区别?
- ArrayList:底层数组,查询遍历快,中间增删慢;
- LinkedList:底层双向链表,首尾增删快,索引查询慢。
泛型的作用?约束集合存储类型,编译期校验,避免类型转换异常,省略强制转换。
增强 for 和迭代器关系?增强 for 底层基于迭代器,是简化写法;本质完全一致。
增强 for 为什么不能遍历删元素?底层迭代器检测到集合长度被修改,触发并发修改异常。
3.3 Day20 核心知识点汇总
- LinkedList:双向链表结构,独有首尾 API,适合头尾高频操作
- 泛型:统一类型、代码安全、简化转型操作
- 迭代器:集合通用遍历,支持遍历中删除元素
- 增强 for:语法简化,底层迭代器,禁止遍历增删
Day20 验收标准(对照自查)
✅ 能口述 ArrayList / LinkedList 底层、优劣、使用场景✅ 熟练手写 LinkedList 首尾增删代码✅ 掌握泛型定义格式、作用、基本使用✅ 独立默写迭代器标准遍历代码✅ 理解增强 for 底层原理 + 并发修改异常原因
关于ArrayList和LinkedList的区分
一、先记两个终极比喻(一辈子不忘)
ArrayList = 电影院一排连坐的座位(动态数组)
- 座位是连在一起的
- 想找第 5 个人,直接跳过去,超快
- 中间加人、减人,后面所有人都要挪位置,超慢
LinkedList = 幼儿园小朋友手拉手(双向链表)
- 每个人只拉着前面和后面的人
- 不占连续空间,随便加人
- 想找第 5 个人,必须从第一个开始数,很慢
- 首尾加人、减人,超级快,只需要松手拉手
二、最核心区别(4 句话背会)
- ArrayList 底层是:动态数组
- LinkedList 底层是:双向链表
- ArrayList:查询快、增删慢
- LinkedList:首尾增删快、查询慢
三、详细对比(一看就懂)
1. 结构区别
ArrayList(动态数组)
- 内存连续、挨着
- 有索引,直接跳转到任意位置
- 满了会自动扩容 1.5 倍
LinkedList(双向链表)
- 内存不连续、分散
- 每个元素存:
前一个人 + 自己 + 后一个人 - 没有扩容,来一个加一个
2. 速度区别(最重要)
表格
| 操作 | ArrayList(数组) | LinkedList(链表) | 原因 |
|---|---|---|---|
| 随机查询(get) | 🟢极快 | 🔴慢 | 数组直接跳;链表要从头数 |
| 中间增删 | 🔴慢 | 🟡一般 | 数组要挪位置;链表只需改拉手 |
| 首尾增删 | 🟡慢 | 🟢极快 | 链表只需要松手、拉手 |
3. 方法区别
ArrayList 通用方法
add、get、remove、set(依靠索引)
LinkedList多了首尾专用方法
addFirst()头加addLast()尾加getFirst()拿头getLast()拿尾removeFirst()删头removeLast()删尾
这些方法ArrayList 没有!
四、开发中到底用谁?(90% 情况都用这个)
开发首选:ArrayList
因为业务中90% 都是查询、展示列表
什么时候用 LinkedList?
- 做队列(排队)
- 做栈(先进后出)
- 频繁头插、尾插
五、终极总结(3 句背会)
- ArrayList = 连续数组 → 查询快、中间增删慢
- LinkedList = 双向链表 → 首尾增删快、查询慢
- 日常开发永远用 ArrayList,特殊场景才用 LinkedList
