编程自学指南:java程序设计开发,List 集合,List 的三大特性,ArrayList动态数组的实现

Java List 集合详解课件

一、课程信息

学习目标

  1. 理解 List 集合的核心特性(有序、可重复、索引访问)
  2. 掌握 ArrayList/LinkedList 的使用场景与区别
  3. 熟练操作 List 的增删改查,避免常见错误
  4. 能通过 List 解决实际问题(如学生管理、购物车)

二、课程导入:数组的痛点与 List 的诞生

❌ 数组的三大痛点

String[] fruits = new String[3]; // 1. 长度固定
fruits[0] = "苹果";
fruits[1] = "香蕉";
// 插入橘子到中间?需手动搬移元素(2. 增删效率低)
// 存储混合类型?Object[]不安全(3. 类型不统一)

✅ List 的解决方案

List<String> fruits = new ArrayList<>(); // 动态扩容
fruits.add("苹果");
fruits.add(1, "橘子"); // 自动扩容+元素移动
// 泛型保证类型安全,只能存String

📌 核心对比:数组 vs List

特性数组List(如 ArrayList)
长度固定动态扩展(按需扩容)
增删效率低(需手动移动元素)高(ArrayList 尾部快,中间慢)
类型安全基本类型 / 对象数组,需强转泛型支持,编译期检查
常用操作索引访问支持更多 API(如 addAll, indexOf)

三、List 基础:核心特性与常用方法

1. List 的三大特性(结合案例)

案例:学生名单管理

List<String> students = new ArrayList<>();
students.add("张三");  // 有序:存入顺序=取出顺序
students.add("李四");
students.add("张三");  // 可重复:允许元素重复
System.out.println(students.get(1)); // 索引访问:李四

2. 常用方法速查表(带案例)

方法名作用案例(以 students 为例)
add(E e)尾部添加元素students.add("王五")
add(int index, E e)插入元素到指定位置students.add(1, "赵六")(索引从 0 开始)
get(int index)获取指定索引的元素System.out.println(students.get(0))
set(int index, E e)修改指定索引的元素students.set(2, "钱七")
remove(int index)删除指定索引的元素students.remove(1)(赵六被删除)
size()获取元素个数System.out.println(students.size())
contains(Object o)判断是否包含元素students.contains("张三") → true
indexOf(Object o)获取元素首次出现的索引students.indexOf("张三") → 0

3. 遍历方式对比(重点!)

案例:打印所有学生

// 方式1:for循环(最常用)
for (int i = 0; i < students.size(); i++) {
    System.out.println(students.get(i));
}

// 方式2:增强for(简洁,无法获取索引)
for (String s : students) {
    System.out.println(s);
}

// 方式3:迭代器(可删除元素)
Iterator<String> it = students.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("张三")) {
        it.remove(); // 安全删除,避免并发异常
    }
}

四、ArrayList:动态数组的实现

1. 内存分配与扩容机制(图解)

初始化new ArrayList<>() 默认容量 10(JDK1.8)。
扩容过程:当元素填满时,扩容为原容量的 1.5 倍(比如 10→15→22...)。
案例:添加 11 个元素时的扩容(黑板画图):

ArrayList<Integer> nums = new ArrayList<>();
for (int i=0; i<11; i++) {
    nums.add(i); // 第11次添加触发扩容
}

2. 增删性能对比(插入位置影响效率)

案例:尾部插入 vs 头部插入

List<String> list = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
    list.add(0, "a"); // 头部插入,每次移动所有元素
}
System.out.println("头部插入耗时:" + (System.currentTimeMillis()-start)); 
// 输出:约3000ms(尾部插入仅需1ms)

3. 适用场景:适合频繁查询、尾部增删的场景

案例:日志系统(按时间顺序添加,很少修改中间元素)。

五、LinkedList:链表的实现

1. 双向链表结构(图解节点)

每个节点包含prevnextvalue,头尾节点的prev/next为 null。
案例:添加 "苹果"→"香蕉"→"橘子",画出链表结构:

null ← [苹果] ↔ [香蕉] ↔ [橘子] → null

2. 增删优势(中间操作快)

案例:在中间插入元素(索引 1)

LinkedList<String> list = new LinkedList<>();
list.add("A");
list.add("B");
list.add(1, "C"); // 仅需修改前后节点的指针,无需移动元素

3. 特有方法(链表专属)

LinkedList<String> queue = new LinkedList<>();
queue.addFirst("加急"); // 头部插入(链表优势)
queue.addLast("普通");  // 尾部插入(等价于add())
System.out.println(queue.getFirst()); // 加急

4. 适用场景:适合频繁中间增删、队列 / 栈场景

案例:聊天消息队列(频繁头部 / 尾部操作)。

六、ArrayList vs LinkedList:对比与选择

操作ArrayListLinkedList
随机访问O (1)(直接索引)O (n)(从头遍历)
尾部增删O (1)(偶尔扩容)O(1)
中间增删O (n)(移动元素)O (1)(改指针)
内存占用连续内存,存储效率高离散内存,每个节点有额外指针
常用场景数组型操作(查询为主)链表型操作(增删为主)

口诀

“查询多用 ArrayList,增删多用 LinkedList,
头尾操作都能行,中间插入 Linked 快!”

七、初学者必避的 5 大陷阱

陷阱 1:索引越界(ArrayIndexOutOfBoundsException)

案例

List<Integer> list = new ArrayList<>();
list.add(1);
System.out.println(list.get(1)); // 报错!索引最大0

解决:先list.size()判断索引范围。

陷阱 2:并发修改异常(ConcurrentModificationException)

错误代码

for (String s : students) { // 增强for内部用迭代器
    if (s.equals("张三")) {
        students.remove(s); // 直接修改集合,触发fail-fast机制
    }
}

正确做法:用迭代器的remove()方法(见遍历方式 3)。

陷阱 3:泛型擦除导致的类型不安全(JDK1.5 前的坑)

错误代码(无泛型):

List list = new ArrayList();
list.add("字符串");
int num = (Integer) list.get(0); // 运行时报ClassCastException

正确:永远使用泛型List<String> list = new ArrayList<>();

陷阱 4:remove(Object)remove(int)的重载歧义

案例

List<Integer> list = Arrays.asList(1,2,3);
list.remove(2); // 意图删除元素2?No!实际删除索引2(元素3)

解决:删除元素时,优先用list.remove((Integer)2)明确类型。

陷阱 5:clear() vs 置null

List<String> list = new ArrayList<>();
list.add("a");
list = null; // ① 引用置null,堆中对象等待GC
list.clear(); // ② 清空元素,集合仍可复用

八、综合案例:学生管理系统

需求:

  1. 添加学生(姓名、学号)
    2. 根据学号删除学生
    3. 修改学生姓名
    4. 查询所有学生

实现代码(带注释):

class Student {
    String name;
    int id;
    public Student(String name, int id) {
        this.name = name;
        this.id = id;
    }
    @Override
    public String toString() {
        return "学号:" + id + ",姓名:" + name;
    }
}

public class StudentManager {
    private List<Student> students = new ArrayList<>();

    // 添加学生(去重:学号唯一)
    public void addStudent(Student stu) {
        for (Student s : students) {
            if (s.id == stu.id) {
                System.out.println("学号已存在!");
                return;
            }
        }
        students.add(stu);
        System.out.println("添加成功!");
    }

    // 根据学号删除学生
    public void deleteStudent(int id) {
        for (Iterator<Student> it = students.iterator(); it.hasNext(); ) {
            Student s = it.next();
            if (s.id == id) {
                it.remove();
                System.out.println("删除成功!");
                return;
            }
        }
        System.out.println("未找到该学号!");
    }

    // 测试
    public static void main(String[] args) {
        StudentManager manager = new StudentManager();
        manager.addStudent(new Student("张三", 1001));
        manager.addStudent(new Student("李四", 1002));
        manager.deleteStudent(1001); // 输出:删除成功!
        System.out.println(manager.students); // 输出:[学号:1002,姓名:李四]
    }
}

九、课堂练习

练习 1:购物车功能(ArrayList 实现)

需求

  • 添加商品(名称、价格)
  • 删除指定名称的商品
  • 计算总价格
  • 打印所有商品(格式:编号 - 名称 - 价格)

提示:用List<Map<String, Object>>或自定义Product类。

练习 2:链表实现栈结构(LinkedList 特有方法)

LinkedList<String> stack = new LinkedList<>();
stack.push("A"); // 入栈(头部添加)
stack.pop(); // 出栈(头部删除)

十、课程总结

知识图谱:

List接口 → 有序、可重复、索引  
   ↳ ArrayList:动态数组,查询快  
   ↳ LinkedList:双向链表,增删快  
核心方法:add/get/set/remove/indexOf  
避坑指南:索引越界、并发修改、泛型缺失  

口诀记忆:

“List 有序能重复,索引访问是基础,
Array 查询快如飞,Linked 增删不挪位,
泛型一定不能少,迭代删除用 it.remove!”

十一、课后作业(必做 + 选做)

必做 1:用 List 实现班级成绩统计

  • 功能:输入 10 个学生成绩,统计平均分、最高分、最低分
  • 提示:ArrayList<Double>存储成绩,Collections.max(list)求最高分

必做 2:链表去重(保留首次出现的元素)

List<Integer> list = Arrays.asList(1, 2, 1, 3, 2);
// 输出:[1, 2, 3]

选做:分析 ArrayList 扩容源码(JDK8)

// 找到grow方法,分析扩容逻辑(1.5倍扩容)
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍
    // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zl515035644

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值