6.1 ArrayList和LinkedList:动态数组vs链表
实验摘要
本实验深入学习Java集合框架中的两个重要类型:ArrayList和LinkedList。内容包括:ArrayList的特点和使用方法(动态数组的优势、元素访问和操作)、LinkedList的特点和应用场景(链式存储的优势、队列和栈操作)、两种集合的性能对比与选择策略、以及丰富的实战编程练习。
通过"可变数组"与"火车车厢"的形象比喻,你将清楚理解两种数据结构的本质差异,学会根据实际需求选择最合适的集合类型。实验提供三个层次的编程练习,从基础的成绩管理到复杂的任务队列系统,帮助你在实际应用中熟练掌握集合操作技巧。
还记得我们之前学过的数组吗?数组虽然很有用,但有一个明显的缺点:大小固定。创建数组时必须指定长度,后期无法改变。
但在实际开发中,我们经常遇到不知道需要存储多少数据的情况。比如:
- 学生成绩(不知道班里有多少学生)
- 购物清单(不知道要买多少样东西)
- 朋友列表(朋友数量会变化)
这时候就需要用到集合(Collection)!今天我们来学习两种最常用的集合:ArrayList 和 LinkedList。
通过本节学习,你将掌握:
- ArrayList - 像"可变长数组",适合频繁查找数据
- LinkedList - 像"链条式存储",适合频繁增删数据
- 选择原则 - 根据实际需求选择合适的集合类型
- 实际应用 - 解决真实的编程问题
一、ArrayList:可变长的"升级版数组"
ArrayList 就像一个会自动扩容的数组。想象一下:
- 普通数组像固定大小的停车场
- ArrayList 像可以随时扩建的停车场
优点:
- ✅ 大小可以动态改变
- ✅ 可以通过索引快速访问元素
- ✅ 提供丰富的操作方法
适用场景:
- 需要频繁访问指定位置的元素
- 数据大小不确定但查询较多
- 需要类似数组的使用体验
基本使用方法
让我们通过一个班级名单的例子来学习 ArrayList 的基本操作:
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) + "个学生");
}
}
注意改进的地方:
- 使用泛型
<String>
指定存储类型 - 添加详细的注释说明每个操作
- 使用更有意义的变量名
- 输出更友好的提示信息
ArrayList 常用方法总结
添加元素:
add(元素)
- 在末尾添加元素add(位置, 元素)
- 在指定位置插入元素
获取元素:
get(索引)
- 获取指定位置的元素size()
- 获取元素总数
删除元素:
remove(索引)
- 删除指定位置的元素remove(元素)
- 删除指定的元素clear()
- 清空所有元素
查找元素:
indexOf(元素)
- 查找元素第一次出现的位置contains(元素)
- 判断是否包含某个元素
其他操作:
isEmpty()
- 判断是否为空set(索引, 新元素)
- 替换指定位置的元素
二、LinkedList:链式存储的"火车模式"
LinkedList 就像一列火车:
- 每节车厢(元素)都知道前一节和后一节车厢在哪里
- 可以很容易地在任意位置加挂或摘掉车厢
- 但要找到特定车厢需要从头开始一节节数过去
优点:
- ✅ 在任意位置插入/删除元素效率高
- ✅ 内存利用率高,按需分配
- ✅ 提供队列和栈的操作方法
适用场景:
- 需要频繁在中间位置插入/删除元素
- 实现队列、栈等数据结构
- 数据大小变化很频繁
基本使用方法
让我们通过一个任务队列的例子来学习 LinkedList:
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 vs LinkedList:如何选择?
理解两者的区别,选择合适的集合类型很重要:
操作 | ArrayList | LinkedList | 推荐使用 |
---|---|---|---|
访问元素 | ⚡ 很快 | 🐌 较慢 | ArrayList |
添加到末尾 | ⚡ 很快 | ⚡ 很快 | 都可以 |
插入到中间 | 🐌 较慢 | ⚡ 很快 | LinkedList |
删除中间元素 | 🐌 较慢 | ⚡ 很快 | LinkedList |
内存占用 | 较少 | 较多 | ArrayList |
选择建议:
- 🔍 查询多,修改少 → 选择 ArrayList
- ✏️ 修改多,查询少 → 选择 LinkedList
- 📊 不确定使用场景 → 优先选择 ArrayList
四、实战练习
练习一:成绩管理系统(ArrayList)
创建一个简单的成绩管理系统:
功能要求:
- 使用 ArrayList 存储学生成绩(整数类型)
- 添加以下成绩:85, 92, 78, 96, 67, 88, 45, 91, 73, 82
- 实现以下功能:
- 计算平均分
- 找出最高分和最低分
- 统计及格人数(≥60分)
- 移除不及格成绩
- 显示最终的成绩列表
参考代码框架:
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. 显示结果
}
}
练习二:任务队列系统(LinkedList)
模拟一个任务处理系统:
功能要求:
- 使用 LinkedList 创建任务队列
- 添加初始任务:[“检查邮件”, “写代码”, “开会”, “测试程序”, “写文档”]
- 模拟任务处理流程:
- 显示当前任务队列
- 处理最优先任务(队列头部)
- 添加紧急任务到队列头部
- 在第3位置插入普通任务
- 完成并移除2个任务
- 显示最终任务队列
参考代码框架:
import java.util.LinkedList;
public class TaskProcessor {
public static void main(String[] args) {
LinkedList<String> taskQueue = new LinkedList<>();
// 1. 添加初始任务
// 2. 显示当前队列
// 3. 处理任务
// 4. 添加紧急任务
// 5. 显示最终结果
}
}
练习三:综合应用(选做)
任务: 创建一个简单的学生信息管理系统
要求:
- 使用 ArrayList 存储学生姓名
- 使用另一个 ArrayList 存储对应的成绩
- 实现功能:
- 添加学生信息
- 查询指定学生的成绩
- 按成绩排序显示学生列表
- 删除指定学生信息
这个练习会让你更深入理解集合的实际应用!
学习小结
✅ ArrayList - 掌握了"可变数组"的使用方法
✅ LinkedList - 学会了"链式存储"的操作技巧
✅ 选择原则 - 能根据场景选择合适的集合类型
✅ 实际应用 - 完成了真实的编程练习
关键要点:
- 🔍 频繁查询 → ArrayList
- ✏️ 频繁修改 → LinkedList
- 📝 泛型让代码更安全
- 🎯 实际开发中优先考虑 ArrayList
恭喜你又掌握了 Java 编程的重要技能!