Java 类与对象 链表
Java面向对象入门与单链表核心实现详解
本文为课堂学习内容的总结与拓展,围绕Java面向对象核心思想,以及单链表的底层原理、完整实现与核心操作进行讲解,兼顾代码解析与知识点补充,适合Java零基础与数据结构入门学习者。
一、面向过程 vs 面向对象
课堂开篇明确了两大编程范式的核心区别:C语言是典型的面向过程编程,Java是典型的面向对象编程,二者的设计思想有着本质差异。
-
面向过程编程
核心是**「做什么,怎么做」**,以功能为核心,将程序拆解为一个个按顺序执行的步骤(函数),聚焦于流程的实现。比如实现链表添加元素,会拆解为「初始化节点→遍历链表→修改指针」等独立的步骤函数,全程围绕执行流程展开。 -
面向对象编程
核心是**「谁来做,做什么」,将现实世界的事物抽象为类(Class),类中封装了属性(数据)和方法(行为)**,程序的执行依靠对象之间的交互完成。
本次课堂的链表实现,就是面向对象思想的典型应用:我们把「节点」抽象为Node类,把「链表」抽象为MyLinkedList类,节点有自己的属性(数据、后继节点引用),链表有自己的属性(头节点)和行为(头插、尾插、删除、查找),所有操作都通过链表对象来完成。
二、链表的底层原理与内存模型
2.1 链表与数组的核心区别
数组和链表都是线性数据结构,但底层内存模型完全不同,适用场景也有明显区分:
| 数据结构 | 内存分布 | 核心优势 | 核心劣势 |
|---|---|---|---|
| 数组 | 连续的内存空间 | 支持随机访问,查询速度快O(1) | 插入删除需移动大量元素,长度固定,内存利用率低 |
| 链表 | 离散的内存空间 | 插入删除灵活,无需移动元素,内存动态分配 | 无法随机访问,只能顺序遍历,查询速度慢O(n) |
2.2 节点的定义与内存理解
链表的最小组成单元是节点(Node),每个节点由两部分构成:
- 数据域:存储当前节点的具体数据
- 指针域(引用域):存储下一个节点的内存地址(Java中为对象引用)
课堂中定义的节点类代码如下,这是链表实现的基础:
// 链表节点类
public class Node {
// 数据域:存储节点的整型数值
public int data;
// 指针域:指向后继节点的引用
public Node next;
// 节点构造方法,创建节点时初始化数据
public Node(int value){
this.data = value;
}
}
内存层面补充讲解:
在Java中,每执行一次new Node(),都会在堆内存中开辟一块独立的空间,存储节点的data和next属性。next本质上是下一个Node对象的内存地址引用,正是通过这个引用,我们才能把内存中离散的多个节点串联成一个完整的链式结构。
比如课堂中的基础示例:
// 创建4个独立的节点对象
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
// 通过next引用串联节点
node1.next = node2;
node2.next = node3;
node3.next = node4;
这段代码会在内存中形成node1 -> node2 -> node3 -> node4 -> null的链式结构,最后一个节点的next为null,表示链表的结束。
三、单链表的完整实现(MyLinkedList)
我们将链表的所有属性和操作封装到MyLinkedList类中,完美体现面向对象的封装特性,下面逐个拆解核心方法的实现逻辑与设计思路。
3.1 链表的核心属性
链表只需要一个**头节点(root/head)**作为访问入口,就能通过next引用遍历到所有节点,这是链表的唯一核心标识。
public class MyLinkedList {
// 链表的头节点(根节点),链表的唯一访问入口
public Node root;
}
3.2 头插法(HeadInsert)
核心原理:将新节点插入到链表的最头部,使其成为新的头节点。
实现步骤:
- 创建新节点,初始化待插入的数值
- 若链表为空(头节点为null),直接将新节点设为头节点
- 若链表非空,让新节点的
next指向当前的头节点 - 将链表的头节点更新为新插入的节点
/**
* 头插法:在链表头部插入新节点
* @param value 待插入的数值
*/
public void HeadInsert(int value){
// 1. 创建新节点
Node newNode = new Node(value);
// 2. 链表为空,新节点直接作为头节点
if(root == null){
root = newNode;
return;
}
// 3. 新节点的next指向原头节点
newNode.next = root;
// 4. 更新头节点为新节点
root = newNode;
}
核心特点:头插法的时间复杂度为O(1),无需遍历链表,插入效率极高;插入顺序与链表存储顺序相反,后插入的元素会出现在链表的最前端。
3.3 尾插法(EndInsert)
核心原理:将新节点插入到链表的最尾部,使其成为链表的最后一个节点。
实现步骤:
- 创建新节点,初始化待插入的数值
- 若链表为空,直接将新节点设为头节点
- 若链表非空,从头节点开始遍历,直到找到
next为null的最后一个节点 - 让最后一个节点的
next指向新节点,完成插入
/**
* 尾插法:在链表尾部插入新节点
* @param value 待插入的数值
*/
public void EndInsert(int value){
Node newNode = new Node(value);
// 链表为空,新节点直接作为头节点
if(root == null){
root = newNode;
return;
}
// 定义临时指针,从头节点开始遍历(不修改原头节点)
Node current = root;
// 遍历到最后一个节点(next为null)
while(current.next != null){
current = current.next;
}
// 最后一个节点的next指向新节点
current.next = newNode;
}
核心特点:尾插法的时间复杂度为O(n),需要遍历整个链表找到尾部;插入顺序与链表存储顺序一致,符合日常的顺序存储习惯。
3.4 链表的遍历打印(Print)
核心原理:从头节点开始,通过临时指针依次遍历每个节点,打印节点数据,直到节点为null时终止。
/**
* 遍历打印链表全量节点
*/
public void Print(){
// 必须使用临时指针遍历,禁止直接移动root,否则会丢失链表入口
Node cur = root;
while(cur != null){
System.out.print(cur.data + "->");
cur = cur.next;
}
// 补充打印链表结束标识null,结构更直观
System.out.println("null");
}
关键注意点:绝对不能直接用头节点root进行遍历移动,否则会丢失链表的入口地址,导致整个链表无法访问。
3.5 获取链表长度(size)
核心原理:遍历链表,通过计数器累加统计节点的总个数。
/**
* 获取链表的节点总数(长度)
* @return 链表长度
*/
public int size(){
int count = 0;
Node cur = root;
while(cur != null){
count++;
cur = cur.next;
}
return count;
}
3.6 指定索引节点删除(delete)
核心原理:找到待删除节点的前驱节点,让前驱节点的next直接指向待删除节点的后继节点,跳过待删除节点,完成删除操作。
课堂中使用了虚拟头节点(dummy节点),这是链表操作的核心技巧,重点讲解:
- 为什么要用虚拟头节点?如果不使用虚拟头节点,当待删除节点是头节点时,需要单独编写逻辑处理;加入虚拟头节点后,无论删除头节点还是中间节点,处理逻辑完全统一,无需额外的边界判断。
/**
* 删除指定索引位置的节点
* @param index 待删除节点的索引(从0开始计数)
*/
public void delete(int index){
// 边界判断:索引小于0,或大于等于链表长度,为非法索引,直接返回
if(index < 0 || index >= size()){
return;
}
// 创建虚拟头节点,统一删除逻辑
Node dummy = new Node(0);
dummy.next = root;
// 临时指针,最终指向待删除节点的前驱节点
Node cur = dummy;
// 遍历index次,找到前驱节点
for(int i = 0; i < index; i++){
cur = cur.next;
}
// 核心删除逻辑:跳过待删除节点
cur.next = cur.next.next;
// 更新头节点,防止原头节点被删除后root失效
root = dummy.next;
}
核心特点:虚拟头节点大幅简化了删除逻辑,降低了边界处理的出错概率;删除操作的时间复杂度为O(n),主要耗时在遍历找到前驱节点。
3.7 元素查找(find)
核心原理:遍历链表,逐个对比节点的数值,找到目标值则返回true,遍历完成仍未找到则返回false。
/**
* 查找链表中是否包含指定数值
* @param value 待查找的数值
* @return 存在返回true,不存在返回false
*/
public boolean find(int value){
Node cur = root;
while(cur != null){
if(cur.data == value){
return true;
}
cur = cur.next;
}
return false;
}
时间复杂度:最好情况O(1)(头节点即为目标值),最坏情况O(n)(目标值在尾部或不存在)。
四、代码测试与运行结果
通过Test类测试链表的核心功能,验证代码的执行效果:
// 链表功能测试类
public class Test {
public static void main(String[] args) {
// 创建链表对象
MyLinkedList list = new MyLinkedList();
// 头插法依次插入1、2、3、4
list.HeadInsert(1);
list.HeadInsert(2);
list.HeadInsert(3);
list.HeadInsert(4);
// 打印初始链表
System.out.println("初始链表(头插法插入1、2、3、4):");
list.Print();
// 删除索引为2的节点
System.out.println("\n删除索引为2的节点后:");
list.delete(2);
list.Print();
// 测试查找功能
System.out.println("\n是否存在数值3:" + list.find(3));
System.out.println("是否存在数值1:" + list.find(1));
// 测试尾插法
list.EndInsert(5);
System.out.println("\n尾插法插入5后:");
list.Print();
}
}
运行结果与分析
- 头插法插入1、2、3、4,由于每次都插入到头部,最终链表顺序为:
4->3->2->1->null - 删除索引为2的节点(索引0=4,1=3,2=2),删除后链表变为:
4->3->1->null - 查找数值3返回
true,查找数值1返回true - 尾插法插入5后,链表最终变为:
4->3->1->5->null
五、课堂内容拓展补充
5.1 课堂代码的细节优化
- 命名规范修正:将原代码的
HeadInseert修正为HeadInsert,符合Java驼峰命名法,提升代码可读性。 - 边界判断优化:将删除方法中原
index>size()修正为index>=size(),因为链表索引从0开始,最大有效索引为size()-1,当index等于size()时已超出合法范围。 - 结构完整性补充:打印方法补充
null结尾标识,更直观地体现链表的结束位置。 - 异常场景修复:删除方法末尾补充
root = dummy.next,防止原头节点被删除后,root仍指向已删除的节点,导致链表结构异常。
5.2 环形链表基础拓展
课堂中提到了环形链表,这里做基础概念补充:
- 什么是环形链表:链表的最后一个节点的
next不再指向null,而是指向链表中的某个节点,形成一个闭环。 - 环形链表的危害:遍历链表时会进入死循环,无法终止,最终导致程序内存溢出崩溃。
- 经典判断方法:快慢指针法(弗洛伊德循环查找算法)
定义两个指针,快指针fast每次走2步,慢指针slow每次走1步;如果链表有环,快慢指针一定会在环内相遇;如果没有环,快指针会先走到链表尾部(null),循环终止。
5.3 链表高频面试考点
基于本次课堂的单链表基础,后续可拓展学习的核心考点:
- 单链表的反转
- 环形链表的判断与入环节点查找
- 合并两个有序链表
- 找到链表的倒数第k个节点
- 两个链表的相交节点查找
六、总结
- 面向对象的核心是抽象与封装,本次链表实现中,我们将节点抽象为
Node类,将链表的属性和操作封装为MyLinkedList类,通过对象交互完成功能,这是与面向过程流程化编程的核心区别。 - 链表的核心本质是节点+引用,通过
next引用将内存中离散的节点串联起来,实现了动态内存管理,插入删除操作比数组更灵活,无需移动大量元素。 - 单链表核心操作的时间复杂度:头插法为O(1),尾插、删除、查找均为O(n),核心逻辑都依赖临时指针的顺序遍历。
- 虚拟头节点是链表操作的关键技巧,能统一头节点与非头节点的处理逻辑,大幅简化代码,减少边界判断的出错概率,是链表相关开发与面试中的必备知识点。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)