Java零基础入门:集合框架:ArrayList 与 LinkedList —— 告别数组的“死板“,拥抱动态容器的“灵活

一、为什么需要集合?

数组的痛点

// 用数组存储学生

Student\[\] students = new Student3;

students0 = new Student("张三", 18);

students1 = new Student("李四", 19);

students2 = new Student("王五", 20);

// 问题来了:

// 1. 想再加一个学生?❌ 数组满了,只能重新创建更大的数组,手动拷贝

// 2. 想删除"李四"?❌ 要手动把后面的元素往前移

// 3. 想知道有几个学生?❌ 得自己维护一个计数器

// 4. 想查找"张三"在哪?❌ 得自己写循环遍历

数组就像固定大小的格子柜​ ------ 买小了不够用,买大了浪费空间,中途换柜子还得自己搬东西。

集合的优势

集合(Collection)是 Java 提供的一套容器框架,可以动态存储、管理一组对象。

对比项 数组 集合(ArrayList)
长度 固定,创建时指定 动态扩容,想加就加
增删元素 手动拷贝、移位 一行代码搞定​
查找 手动遍历 内置方法(contains、indexOf)
类型安全 可以存基本类型 只能存对象(JDK5+ 有自动装箱)
功能 只有 length 几十种实用方法​

二、ArrayList ------ 动态数组

什么是 ArrayList?

ArrayList 是 List 接口的实现类,底层是一个可以自动扩容的数组。

生活类比:

ArrayList 就像一个可以自动伸缩的购物袋:

├── 买东西少 → 袋子收缩,不占地方

├── 买东西多 → 袋子自动变大,装得下

├── 想拿第3个商品 → 直接伸手进去拿(按索引访问,快!)

└── 想加商品 → 直接往里放(自动扩容)

创建 ArrayList

import java.util.ArrayList;

import java.util.List;

// 方式1:推荐写法(面向接口)

List<String> list1 = new ArrayList<>();

// 方式2:直接写实现类

ArrayList<Integer> list2 = new ArrayList<>();

// 方式3:指定初始容量(知道大概要存多少时)

ArrayList<String> list3 = new ArrayList<>(20);

⚠️ 泛型:<String> 表示这个 ArrayList 只能存 String 类型的对象。这是类型安全的保障。

ArrayList 常用方法大全

方法 说明 示例
add(E e) 在末尾添加元素 list.add("张三")
add(int index, E e) 在指定位置插入 list.add(0, "李四")
get(int index) 获取指定位置元素 list.get(0)
set(int index, E e) 修改指定位置元素 list.set(0, "王五")
remove(int index) 删除指定位置元素 list.remove(0)
remove(Object o) 删除指定元素 list.remove("张三")
size() 获取元素个数 list.size()
isEmpty() 判断是否为空 list.isEmpty()
contains(Object o) 判断是否包含某元素 list.contains("张三")
indexOf(Object o) 查找元素位置 list.indexOf("张三")
clear() 清空所有元素 list.clear()
toArray() 转为数组 list.toArray()

遍历 ArrayList 的三种方式

ArrayList<String> list = new ArrayList<>();

list.add("苹果");

list.add("香蕉");

list.add("橙子");

// 方式1:普通 for 循环(需要索引时用)

for (int i = 0; i < list.size(); i++) {

System.out.println(list.get(i));

}

// 方式2:增强 for 循环(最常用,简洁)

for (String fruit : list) {

System.out.println(fruit);

}

// 方式3:迭代器(后面学,了解即可)

Iterator<String> it = list.iterator();

while (it.hasNext()) {

System.out.println(it.next());

}

ArrayList 底层原理(通俗版)

初始状态:内部数组容量 = 10(默认)

null, null, null, null, null, null, null, null, null, null

添加第1个元素 "张三":

"张三", null, null, ...

添加第11个元素时:

内部数组满了 → 自动创建新数组(容量变为 15 = 10 + 10/2)

→ 把旧数组元素拷贝到新数组 → 继续添加

扩容机制 :默认初始容量 10,满了之后扩容为原来的 1.5 倍。


三、编码实战:学生管理系统(ArrayList CRUD)

需求

功能清单:

  1. 添加学生(Create)

  2. 查看所有学生(Read)

  3. 修改学生信息(Update)

  4. 删除学生(Delete)

  5. 按学号查找学生

  6. 退出系统

完整代码

import java.util.ArrayList;

import java.util.List;

import java.util.Scanner;

// ========== 学生类 ==========

class Student {

private String id; // 学号

private String name; // 姓名

private int age; // 年龄

private String major; // 专业

public Student() {

}

public Student(String id, String name, int age, String major) {

this.id = id;

this.name = name;

this.age = age;

this.major = major;

}

// Getter 和 Setter

public String getId() { return id; }

public void setId(String id) { this.id = id; }

public String getName() { return name; }

public void setName(String name) { this.name = name; }

public int getAge() { return age; }

public void setAge(int age) { this.age = age; }

public String getMajor() { return major; }

public void setMajor(String major) { this.major = major; }

// 重写 toString(),方便打印

@Override

public String toString() {

return "学号:" + id + ",姓名:" + name + ",年龄:" + age + ",专业:" + major;

}

}

// ========== 学生管理类(核心 CRUD) ==========

class StudentManager {

// 用 ArrayList 存储学生

private List<Student> students = new ArrayList<>();

// 1. 添加学生

public boolean addStudent(Student student) {

// 检查学号是否已存在

for (Student s : students) {

if (s.getId().equals(student.getId())) {

System.out.println("❌ 学号 " + student.getId() + " 已存在!");

return false;

}

}

students.add(student);

System.out.println("✅ 添加成功!");

return true;

}

// 2. 查看所有学生

public void showAllStudents() {

if (students.isEmpty()) {

System.out.println("📭 暂无学生信息");

return;

}

System.out.println("\n====== 学生列表(共 " + students.size() + " 人)======");

for (int i = 0; i < students.size(); i++) {

System.out.println((i + 1) + ". " + students.get(i));

}

System.out.println("========================================\n");

}

// 3. 按学号查找学生

public Student findById(String id) {

for (Student s : students) {

if (s.getId().equals(id)) {

return s;

}

}

return null;

}

// 4. 修改学生信息

public boolean updateStudent(String id, String newName, int newAge, String newMajor) {

Student student = findById(id);

if (student == null) {

System.out.println("❌ 未找到学号为 " + id + " 的学生");

return false;

}

if (newName != null && !newName.trim().isEmpty()) {

student.setName(newName);

}

if (newAge > 0) {

student.setAge(newAge);

}

if (newMajor != null && !newMajor.trim().isEmpty()) {

student.setMajor(newMajor);

}

System.out.println("✅ 修改成功!");

return true;

}

// 5. 删除学生

public boolean deleteStudent(String id) {

Student student = findById(id);

if (student == null) {

System.out.println("❌ 未找到学号为 " + id + " 的学生");

return false;

}

students.remove(student);

System.out.println("✅ 删除成功!");

return true;

}

// 6. 获取学生总数

public int getCount() {

return students.size();

}

}

// ========== 测试类 ==========

public class StudentManagementSystem {

public static void main(String\[\] args) {

StudentManager manager = new StudentManager();

Scanner scanner = new Scanner(System.in);

// 预添加几个学生

manager.addStudent(new Student("S001", "张三", 20, "计算机科学"));

manager.addStudent(new Student("S002", "李四", 21, "软件工程"));

manager.addStudent(new Student("S003", "王五", 19, "人工智能"));

while (true) {

System.out.println("\n====== 🎓 学生管理系统 ======");

System.out.println("1. 添加学生");

System.out.println("2. 查看所有学生");

System.out.println("3. 查找学生(按学号)");

System.out.println("4. 修改学生信息");

System.out.println("5. 删除学生");

System.out.println("6. 退出");

System.out.print("请选择操作:");

int choice = scanner.nextInt();

scanner.nextLine(); // 消耗换行符

switch (choice) {

case 1:

System.out.print("请输入学号:");

String id = scanner.nextLine();

System.out.print("请输入姓名:");

String name = scanner.nextLine();

System.out.print("请输入年龄:");

int age = scanner.nextInt();

scanner.nextLine();

System.out.print("请输入专业:");

String major = scanner.nextLine();

manager.addStudent(new Student(id, name, age, major));

break;

case 2:

manager.showAllStudents();

break;

case 3:

System.out.print("请输入要查找的学号:");

String findId = scanner.nextLine();

Student s = manager.findById(findId);

if (s != null) {

System.out.println("✅ 找到:" + s);

} else {

System.out.println("❌ 未找到该学生");

}

break;

case 4:

System.out.print("请输入要修改的学生学号:");

String updateId = scanner.nextLine();

System.out.print("请输入新姓名(不修改直接回车):");

String newName = scanner.nextLine();

System.out.print("请输入新年龄(不修改输入0):");

int newAge = scanner.nextInt();

scanner.nextLine();

System.out.print("请输入新专业(不修改直接回车):");

String newMajor = scanner.nextLine();

manager.updateStudent(updateId, newName, newAge, newMajor);

break;

case 5:

System.out.print("请输入要删除的学生学号:");

String deleteId = scanner.nextLine();

manager.deleteStudent(deleteId);

break;

case 6:

System.out.println("👋 再见!");

scanner.close();

System.exit(0);

default:

System.out.println("❌ 无效选择,请重新输入");

}

}

}

}

运行效果示例

====== 🎓 学生管理系统 ======

  1. 添加学生

  2. 查看所有学生

  3. 查找学生(按学号)

  4. 修改学生信息

  5. 删除学生

  6. 退出

请选择操作:2

====== 学生列表(共 3 人)======

  1. 学号:S001,姓名:张三,年龄:20,专业:计算机科学

  2. 学号:S002,姓名:李四,年龄:21,专业:软件工程

  3. 学号:S003,姓名:王五,年龄:19,专业:人工智能

========================================

核心知识点解析

要点 说明
List<Student> 面向接口编程,用接口类型引用 ArrayList
students.add() 动态添加,无需关心数组大小
students.remove() 传入对象直接删除,ArrayList 自动移位
students.isEmpty() 判断是否为空,避免空指针
findById() 遍历查找,返回 null 表示没找到
toString() 重写 System.out.println(student) 时自动调用

四、LinkedList ------ 双向链表

什么是 LinkedList?

LinkedList 是 List 接口的另一个实现类,底层是双向链表。

生活类比:

ArrayList 像一排连续的小格子,每个格子有编号,找第n个直接定位(快)

但中间插入/删除要挪动后面所有元素(慢)

LinkedList 像一列火车,每节车厢连着前后

中间插入/删除只需改连接(快)

但找第n节要从头/尾开始数(慢)

LinkedList 的独门绝技:队列和栈

LinkedList 除了实现 List 接口,还实现了 Queue(队列)接口,天然支持队列和栈操作。

模拟队列(FIFO:先进先出)

队列就像排队买票:先来的人先买,后来的人站后面。

import java.util.LinkedList;

public class QueueDemo {

public static void main(String\[\] args) {

LinkedList<String> queue = new LinkedList<>();

// 入队

queue.offer("张三");

queue.offer("李四");

queue.offer("王五");

System.out.println("队列内容:" + queue);

// 输出:张三, 李四, 王五

// 查看队首(不删除)

System.out.println("队首是谁:" + queue.peek());

// 输出:张三

// 出队

String first = queue.poll();

System.out.println("出队:" + first);

// 输出:出队:张三

System.out.println("出队后队列:" + queue);

// 输出:李四, 王五

// 继续出队直到空

System.out.println("出队:" + queue.poll()); // 李四

System.out.println("出队:" + queue.poll()); // 王五

System.out.println("出队(空队列):" + queue.poll()); // null(不会抛异常)

}

}

模拟栈(LIFO:后进先出)

栈就像一摞盘子:只能从最上面放、从最上面取。

import java.util.LinkedList;

public class StackDemo {

public static void main(String\[\] args) {

LinkedList<Integer> stack = new LinkedList<>();

// 入栈(push)

stack.push(10);

stack.push(20);

stack.push(30);

System.out.println("栈内容:" + stack);

// 输出:30, 20, 10 (30在栈顶)

// 查看栈顶(不弹出)

System.out.println("栈顶元素:" + stack.peek());

// 输出:30

// 出栈(pop)

int top = stack.pop();

System.out.println("出栈:" + top);

// 输出:出栈:30

System.out.println("出栈后栈内容:" + stack);

// 输出:20, 10

// 继续出栈

System.out.println("出栈:" + stack.pop()); // 20

System.out.println("出栈:" + stack.pop()); // 10

// stack.pop(); // ❌ 空栈时调用会抛 NoSuchElementException

System.out.println("栈是否为空:" + stack.isEmpty()); // true

}

}

LinkedList 作为 List 使用

LinkedList<String> list = new LinkedList<>();

list.add("A"); // 尾部添加

list.addFirst("B"); // 头部添加

list.addLast("C"); // 尾部添加(同 add)

list.add(1, "D"); // 在索引1处插入

System.out.println(list.get(0)); // B

System.out.println(list.getFirst()); // B

System.out.println(list.getLast()); // C

list.removeFirst(); // 删除头部

list.removeLast(); // 删除尾部

LinkedList 常用方法速查

方法 说明 队列/栈角色
add(e) / addLast(e) 尾部添加 入队 / 入栈
addFirst(e) 头部添加 入栈
offer(e) 尾部添加(推荐队列用) 入队
poll() 删除并返回头部 出队
peek() 查看头部(不删除) 查看队首/栈顶
push(e) 头部压入 入栈
pop() 删除并返回头部 出栈
removeFirst() 删除头部 出栈/出队
removeLast() 删除尾部 -
getFirst() / getLast() 获取头/尾 -

五、ArrayList vs LinkedList ------ 到底用哪个?

对比速查表

维度 ArrayList LinkedList
底层结构 动态数组​ 双向链表​
随机访问 get(i) ⚡ O(1) 极快​ 🐢 O(n) 慢
尾部添加 add(e) ⚡ 平均 O(1) ⚡ O(1)
中间插入 add(i, e) 🐢 O(n) 要移位 ⚡ O(1)(找到位置后)
中间删除 remove(i) 🐢 O(n) 要移位 ⚡ O(1)(找到位置后)
内存占用 较少(只存数据) 较多(每个节点额外存前后指针)
适用场景 查询多、增删少​ 增删多、查询少​
额外功能 无 天然支持队列、栈操作

选择指南

问自己:

"我的程序主要是做什么?"

├── 大量按索引查找、遍历 → ArrayList ✅

├── 大量在中间插入/删除 → LinkedList ✅

├── 需要当队列/栈用 → LinkedList ✅

└── 不确定 → 默认用 ArrayList ✅(90% 场景选它)


六、每日一练

练习1(基础):ArrayList 练习

  • 创建一个 ArrayList<String> 存储 5 个你喜欢的电影名
  • 在第 2 个位置插入一部新电影
  • 删除最后一部电影
  • 遍历打印所有电影名

练习2(进阶):图书管理系统

  • 定义 Book 类(书名、作者、价格)
  • 用 ArrayList<Book> 存储图书
  • 实现:添加图书、按书名查找、修改价格、删除图书、显示所有图书

练习3(挑战):LinkedList 综合

  • 用 LinkedList 模拟一个"浏览器历史记录"
    • 访问新页面 → push() 入栈
    • 点击"后退" → pop() 出栈
    • 显示当前页面 → peek()
    • 显示所有历史记录
  • 同时用同一个 LinkedList 模拟一个"打印任务队列"
    • 添加打印任务 → offer()
    • 执行打印 → poll()
    • 查看下一个任务 → peek()

一句话记住本章:ArrayList 是"查得快"的动态数组,LinkedList 是"改得快"的双向链表;ArrayList 适合日常 CRUD,LinkedList 适合队列和栈。

需要我提供课后练习的完整解答,还是想继续深入下一个知识点(如HashMap 、异常处理 、泛型深入)?随时告诉我!

相关推荐
qeen871 小时前
【QT】 信号与槽初始
开发语言·笔记·qt·学习
FYKJ_20101 小时前
springboot游泳馆管理系统79222-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·mysql·django·课程设计
艾莉丝努力练剑2 小时前
【AI大模型接入SDK】C++ ChatSDK使用手册
开发语言·网络·c++·人工智能·学习·大模型
panxianren9 小时前
Bun 1.4 的 bun test --parallel 将 4 秒的测试套件缩短到约 1 秒
java·javascript
“AI国潮设计-小江”10 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
程序喵大人10 小时前
【C++入门】编译链接模型 - 01 一个 C++ 程序是怎样变成可执行文件的
开发语言·c++·编译链接模型
sunshine22 girl10 小时前
Java学习七 Java 项目实战2-注册业务
java·学习
wenwen_chao11 小时前
Python学习与开发资源合集:实用技巧与工具推荐
python
省钱兄--zs11 小时前
北京24小时自助健身房系统软件开发实战:从架构设计到部署指南
java·数据仓库·spring boot·小程序·需求分析