6.1 ArrayList和LinkedList:动态数组vs链表

系列 - JAVA集合
摘要

实验摘要

本实验深入学习Java集合框架中的两个重要类型:ArrayList和LinkedList。内容包括:ArrayList的特点和使用方法(动态数组的优势、元素访问和操作)、LinkedList的特点和应用场景(链式存储的优势、队列和栈操作)、两种集合的性能对比与选择策略、以及丰富的实战编程练习。

通过"可变数组"与"火车车厢"的形象比喻,你将清楚理解两种数据结构的本质差异,学会根据实际需求选择最合适的集合类型。实验提供三个层次的编程练习,从基础的成绩管理到复杂的任务队列系统,帮助你在实际应用中熟练掌握集合操作技巧。

还记得我们之前学过的数组吗?数组虽然很有用,但有一个明显的缺点:大小固定。创建数组时必须指定长度,后期无法改变。

但在实际开发中,我们经常遇到不知道需要存储多少数据的情况。比如:

  • 学生成绩(不知道班里有多少学生)
  • 购物清单(不知道要买多少样东西)
  • 朋友列表(朋友数量会变化)

这时候就需要用到集合(Collection)!今天我们来学习两种最常用的集合:ArrayList 和 LinkedList。

学习目标

通过本节学习,你将掌握:

  1. ArrayList - 像"可变长数组",适合频繁查找数据
  2. LinkedList - 像"链条式存储",适合频繁增删数据
  3. 选择原则 - 根据实际需求选择合适的集合类型
  4. 实际应用 - 解决真实的编程问题

ArrayList 就像一个会自动扩容的数组。想象一下:

  • 普通数组像固定大小的停车场
  • ArrayList 像可以随时扩建的停车场
ArrayList 的特点

优点:

  • ✅ 大小可以动态改变
  • ✅ 可以通过索引快速访问元素
  • ✅ 提供丰富的操作方法

适用场景:

  • 需要频繁访问指定位置的元素
  • 数据大小不确定但查询较多
  • 需要类似数组的使用体验

让我们通过一个班级名单的例子来学习 ArrayList 的基本操作:

java

import java.util.ArrayList;  
  
public class StudentList {  
    public static void main(String[] args) {  
        // 1. 创建一个 ArrayList 来存储学生姓名
        ArrayList<String> students = new ArrayList<>();  
        
        // 2. 添加学生姓名
        students.add("张三");  
        students.add("李四");  
        students.add("王五");  
        students.add("赵六");  
        students.add("钱七");  
        students.add("孙八");  
  
        // 3. 查看班级人数
        System.out.println("班级总人数:" + students.size());  
        
        // 4. 查看指定位置的学生(注意:位置从0开始)
        System.out.println("第2个学生是:" + students.get(1));  // 李四
        
        // 5. 遍历所有学生
        System.out.println("班级名单:");
        for (int i = 0; i < students.size(); i++) {  
            System.out.print(students.get(i) + "\t");  
        }  
        System.out.println();  
  
        // 6. 删除转学的学生
        students.remove(1);  // 删除李四
        System.out.println("李四转学后的名单:");
        for (int i = 0; i < students.size(); i++) {  
            System.out.print(students.get(i) + "\t");  
        }  
  
        // 7. 查找学生的位置
        int position = students.indexOf("赵六");
        System.out.println("\n赵六现在是第" + (position + 1) + "个学生");  
    }  
}
代码改进说明

注意改进的地方:

  1. 使用泛型 <String> 指定存储类型
  2. 添加详细的注释说明每个操作
  3. 使用更有意义的变量名
  4. 输出更友好的提示信息
ArrayList 方法大全

添加元素:

  • add(元素) - 在末尾添加元素
  • add(位置, 元素) - 在指定位置插入元素

获取元素:

  • get(索引) - 获取指定位置的元素
  • size() - 获取元素总数

删除元素:

  • remove(索引) - 删除指定位置的元素
  • remove(元素) - 删除指定的元素
  • clear() - 清空所有元素

查找元素:

  • indexOf(元素) - 查找元素第一次出现的位置
  • contains(元素) - 判断是否包含某个元素

其他操作:

  • isEmpty() - 判断是否为空
  • set(索引, 新元素) - 替换指定位置的元素

LinkedList 就像一列火车

  • 每节车厢(元素)都知道前一节和后一节车厢在哪里
  • 可以很容易地在任意位置加挂或摘掉车厢
  • 但要找到特定车厢需要从头开始一节节数过去
LinkedList 的特点

优点:

  • ✅ 在任意位置插入/删除元素效率高
  • ✅ 内存利用率高,按需分配
  • ✅ 提供队列和栈的操作方法

适用场景:

  • 需要频繁在中间位置插入/删除元素
  • 实现队列、栈等数据结构
  • 数据大小变化很频繁

让我们通过一个任务队列的例子来学习 LinkedList:

java

import java.util.LinkedList;  
  
public class TaskQueue {  
    public static void main(String[] args) {  
        // 1. 创建一个任务队列
        LinkedList<String> tasks = new LinkedList<>();  
        
        // 2. 添加初始任务
        tasks.add("完成作业");  
        tasks.add("整理房间");  
        tasks.add("购买食材");  
        tasks.add("准备考试");  
        tasks.add("运动锻炼");  
        tasks.add("阅读书籍");  
  
        System.out.println("初始任务列表:" + tasks);  
        
        // 3. 在指定位置插入紧急任务
        tasks.add(3, "紧急会议");  
        System.out.println("插入紧急任务后:" + tasks);  
        
        // 4. 在队列最前面添加最优先任务
        tasks.addFirst("处理邮件");  
        System.out.println("添加最优先任务后:" + tasks);  
        
        // 5. 查看队列最前面的任务
        System.out.println("当前最优先任务:" + tasks.getFirst());  
        
        // 6. 完成并移除指定任务
        tasks.remove(3);  // 移除第4个任务
        System.out.println("完成一项任务后:" + tasks);  
        
        // 7. 完成最优先任务
        String completedTask = tasks.removeFirst();  
        System.out.println("完成任务:" + completedTask);
        System.out.println("当前任务列表:" + tasks);  
    }  
}

LinkedList 除了有普通集合的方法外,还有一些特殊的方法:

LinkedList 特有方法

队列头部操作:

  • addFirst(元素) - 在头部添加元素
  • getFirst() - 获取头部元素
  • removeFirst() - 删除并返回头部元素

队列尾部操作:

  • addLast(元素) - 在尾部添加元素
  • getLast() - 获取尾部元素
  • removeLast() - 删除并返回尾部元素

模拟栈操作:

  • push(元素) - 压入栈顶(相当于 addFirst)
  • pop() - 弹出栈顶(相当于 removeFirst)
  • peek() - 查看栈顶(相当于 getFirst)

理解两者的区别,选择合适的集合类型很重要:

性能对比
操作 ArrayList LinkedList 推荐使用
访问元素 ⚡ 很快 🐌 较慢 ArrayList
添加到末尾 ⚡ 很快 ⚡ 很快 都可以
插入到中间 🐌 较慢 ⚡ 很快 LinkedList
删除中间元素 🐌 较慢 ⚡ 很快 LinkedList
内存占用 较少 较多 ArrayList

选择建议:

  • 🔍 查询多,修改少 → 选择 ArrayList
  • ✏️ 修改多,查询少 → 选择 LinkedList
  • 📊 不确定使用场景 → 优先选择 ArrayList
任务要求

创建一个简单的成绩管理系统:

功能要求:

  1. 使用 ArrayList 存储学生成绩(整数类型)
  2. 添加以下成绩:85, 92, 78, 96, 67, 88, 45, 91, 73, 82
  3. 实现以下功能:
    • 计算平均分
    • 找出最高分和最低分
    • 统计及格人数(≥60分)
    • 移除不及格成绩
    • 显示最终的成绩列表

参考代码框架:

java

import java.util.ArrayList;

public class ScoreManager {
    public static void main(String[] args) {
        ArrayList<Integer> scores = new ArrayList<>();
        
        // 1. 添加成绩
        scores.add(85);
        scores.add(92);
        // ... 继续添加其他成绩
        
        // 2. 计算平均分
        
        // 3. 找出最高分和最低分
        
        // 4. 统计及格人数
        
        // 5. 移除不及格成绩
        
        // 6. 显示结果
    }
}
任务要求

模拟一个任务处理系统:

功能要求:

  1. 使用 LinkedList 创建任务队列
  2. 添加初始任务:[“检查邮件”, “写代码”, “开会”, “测试程序”, “写文档”]
  3. 模拟任务处理流程:
    • 显示当前任务队列
    • 处理最优先任务(队列头部)
    • 添加紧急任务到队列头部
    • 在第3位置插入普通任务
    • 完成并移除2个任务
    • 显示最终任务队列

参考代码框架:

java

import java.util.LinkedList;

public class TaskProcessor {
    public static void main(String[] args) {
        LinkedList<String> taskQueue = new LinkedList<>();
        
        // 1. 添加初始任务
        
        // 2. 显示当前队列
        
        // 3. 处理任务
        
        // 4. 添加紧急任务
        
        // 5. 显示最终结果
    }
}
挑战任务

任务: 创建一个简单的学生信息管理系统

要求:

  1. 使用 ArrayList 存储学生姓名
  2. 使用另一个 ArrayList 存储对应的成绩
  3. 实现功能:
    • 添加学生信息
    • 查询指定学生的成绩
    • 按成绩排序显示学生列表
    • 删除指定学生信息

这个练习会让你更深入理解集合的实际应用!

本节收获

ArrayList - 掌握了"可变数组"的使用方法
LinkedList - 学会了"链式存储"的操作技巧
选择原则 - 能根据场景选择合适的集合类型
实际应用 - 完成了真实的编程练习

关键要点:

  • 🔍 频繁查询 → ArrayList
  • ✏️ 频繁修改 → LinkedList
  • 📝 泛型让代码更安全
  • 🎯 实际开发中优先考虑 ArrayList

恭喜你又掌握了 Java 编程的重要技能!

相关内容