双向链表的初步练习

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:  Solitary-walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互赞互关一下,看到必回
👑👑👑💎👑👑👑   

目录

 


1. 双向链表的初始化

2. 双向链表的销毁

3. 双向链表的尾插

4. 双向链表的头插

5. 双向链表的尾删

6.双向链表的头删

7. 双向链表的指定位置之后的插入

8. 双向链表的指定位置的删除
9. 双向链表的按值查找

10.链表打印


首先在我们进行各项具体任务之前,咱还是需要把前期工作准备好的 

1.先进行自定义链表的声明

2.一些函数的声明

3.必要头文件的引入

首先我们要知道什么是双向链表,顾名思义,对于一个结点而言既有指向自己的指针,也有指向其他结点的指针域

如图所示:

链表结构体类型的定义代码如下:

typedef struct ListNode 
{
	DataType data;//数据域
	struct ListNode* pre;//前驱域
	struct ListNode*next;//后继域
}ListNode;

 1.初始化

 注意这里的带头双向链表不同于前面单链表的头节点

为了好区分,这里我们称之为"哨兵位"

哨兵位只是占一个位置,并不存储任何有效的数据

 当我们只有哨兵位这个结点,思考一下,如何变成一个双向循环链表

你想对了吗??

	phead->pre = phead->next = phead;//让他变成双向循环链表

初始化代码之不采用 传参的方法

ListNode* Init()//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
{
	//自己需要创建一个哨兵位
	ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
	assert(phead);//可能开辟失败
	phead->data = -1;
	phead->pre = phead->next = phead;//记住这里要让他变成双向循环链表
	return phead;
}

初始化代码之采用 传参的方法

注意这里我们形参是用二级指针来接收的

因为实参我们需要传链表的地址,否则我们是无法完成初始化的

因为函数形参只是实参的一份临时拷贝,对形参的临时修改并不会影响我们的实参

void Init(ListNode** pphead)//初始化,采用传参的形式
{
	//注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置,并不存储任何有效数据
	assert(pphead);
	*pphead = (ListNode*)malloc(sizeof(ListNode));
	if (*pphead == NULL)
	{
		perror("malloc fail\n");
		return 1;//既然开辟失败,没必要执行下面代码
	}

	(*pphead)->data = -1;//注意优先级顺序
	(*pphead)->next = (*pphead)->pre = NULL;//构建一个双向循环链表
}
2.销毁

说白了,其实就是一个结点一个结点进行删除

这自然就需要遍历了

我们在删除之前需要先找到删除结点后一个结点

关键是我们实参到底是指针还是指针地址

 让要删除的结点为 del = phead->next;

那么要删除的结点下一个结点为 del->next

我们不妨试一下~~~

实参为指针

void Destroy(ListNode* phead)//链表销毁;想一下,传一级指针还是二级?  在这里我们传入一级指针,为了保持接口一致性
{
	//销毁我们是一个一个进行删除,自然就需要遍历
	assert(phead);
	ListNode* del = phead->next;
	while (del != phead)
	{
		ListNode* next = del->next;
		free(del);
		/*del = NULL;*/    //  ?
		del = next;
	}
	//来到这说明,此时只有一个哨兵位
	free(phead)
    phead = NULL;

}

 注意当我们只是简单调用销毁函数的时候,代码并不是我们所设想的一样

正常来说,我们的plist的值应该为NULL

 

为了解决这个问题我们就需要在传一级指针的时候,手动把plist 置为空 

 实参为指针地址

 代码如下,但是问题又来了

void LTDestroy(ListNode** pphead) 
{
		assert(pphead && *pphead);
		ListNode* cur = (*pphead)->next;
		while ( cur!=*pphead )
		{
			ListNode* next = cur->next;
			free(cur);
			cur = next;
		}
		free(*pphead);
		*pphead = NULL;
	}

 对于这个问题容本up主先买个关子,欲知后事如何 且听下回分解

为了保持接口一致性,我们这里就使用一级指针来接收

3.尾插

顾名思义,是在尾结点后面进行插入,注意这里在哨兵位的前面进行插入也是可以滴~~~

接下来就是改变指针的指向

1)首先既然是尾插就需要为要插进来的数据开辟结点

这里涉及到创建结点的函数

2)其次是处理指针指向

3)先处理node的pre ,next

4)处理 原来尾结点,哨兵位

 尾插草图如下:

 结点创建函数的代码如下:

ListNode* ListBuyNode(x)//创建结点
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	//来到这,说明空间开辟成功
	node->data = x;
	node->next = node->pre = NULL;
	return node;
}

 

 

 尾插完整代码:

void PushBack(ListNode* phead,DataType x)//尾插
{
	assert(phead);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//先处理node 的前驱,后继
	node->pre = phead->pre; //  phead->pre->next原来尾结点的后继,node->pre = phead->pre->next这里出现了野指针的问题 phead->pre->next没有具体指向
	node->next = phead;
	//接下来在处理 原来尾结点,哨兵位
	/*,这样写是错的,以下2句话不可以颠倒
	已经把node这个结点给覆盖掉了,在执行65行代码时,node 是未知的,因为这个结点已经被覆盖了,phead->pre->next此时他的指向也就是未知的,调试时不会报错,但是遍历读取他就会报错了,就像你在打印函数里进行打印时,出现野指针
	phead->pre = node;
	phead->pre->next = node;*/

	phead->pre->next = node;
	phead->pre = node;//别忘了,每尾插进来的结点最终都是一个新的尾结点
}

4.头插

首先是在哨兵位是后面进行的,每插入进来应该结点,此节点就会成为一个新的头节点

同上,需要先创建一个结点

其次找到原来头节点 phead->next 

 头插草图如下:

头插完整代码如下:

void PushFront(ListNode* phead,DataType x)//头插
{
	//头插是在哨兵位的后面进行插入
	assert(phead);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//处理node 的后继,前驱
	node->pre = phead;
	node->next = phead->next;
	//处理phead ,phead->next 
	/*这样写也是对的
	phead->next->pre = node;*/
	phead->next = node;//头节点要进行更新
	phead->next->pre = node;

}

5.尾删

注意我们不能上来就行删除

需要先找到要删除结点 (del)前一个也就是 del->pre

这里我们一定要注意结点的先后顺序

完整代码如下:

void PopBack(ListNode* phead)//尾删
{
	//注意不能直接删除,需要先找到尾结点的前一个结点
	assert(phead);
	//先判断以下是否为空,有2种写法
	/*if (phead->pre == phead)
	{
		return 9;
	}*/
	assert(phead->next != phead);//是否为空
	ListNode* del = phead->pre;//把要删除的结点先保存起来
	//删除之前,先要构成一个新的双向循环链表
	del->pre->next = phead;
	phead->pre = del->pre;
	/*错误的,顺序不能颠倒,因为此时 del->pre已经被覆盖了
	phead->pre = del->pre;//新的尾结点
	del->pre->next = phead;*/

	free(del);
	del = NULL;

}

 

7. 指定位置之后的插入
草图如下:

 

1)要为插入的数据创建结点

2)找到指定位置(pos)之后的结点 (pos->next)和之前的结点(pos->pre

3)  改变指针指向

指定位置之后插入对应完整代码:

void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{
	assert(pos);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//处理 node 的前驱,后继
	node->next = pos->next;
	node->pre = pos;
	//处理 pos 和 pos->next的pre
	pos->next = node;
	pos->next->pre = node;

}
8. 双向链表的指定位置的删除

对应草图如下:

 直接进行删除是不行滴~~~

先要找到指定位置(pos)之后的结点 (pos->next)和之前的结点(pos->pre

其次改变指针走向

void Earse(ListNode* pos)//指定位置删除
{
	assert(pos);
	//需要找到pos之前的一个结点和之后的一个结点
	pos->next->pre = pos->pre;
	pos->pre->next = pos->next;
	free(pos);
	pos = NULL;
}
9. 双向链表的按值查找

 这里自然就需要一个一个进行遍历了

按值查找对应完整代码:

ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{
	assert(phead);
	ListNode* pcur = phead->next;//定义一个用来循环遍历的指针
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;//直接返回对应结点
		}
		pcur = pcur->next;
	}
	printf("没有找到\n");
}

 10链表打印

方法同上,对链表进行遍历

void Print(ListNode* phead)//链表打印
{
	assert(phead);
	ListNode* pcur = phead->next;//定义一个用来 遍历的指针,注意他初始值是 phead->next
	while (pcur != phead)
	{
		printf("%d-> ", pcur->data);
		pcur = pcur->next;//记得更新
	}
	printf("\n");
}

 


List.c对应完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"

//void Init(ListNode** pphead)//初始化,采用传参的形式
//{
//	//注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置,并不存储任何有效数据
//	assert(pphead);
//	*pphead = (ListNode*)malloc(sizeof(ListNode));
//	if (*pphead == NULL)
//	{
//		perror("malloc fail\n");
//		return 1;//既然开辟失败,没必要执行下面代码
//	}
//
//	(*pphead)->data = -1;//注意优先级顺序
//	(*pphead)->next = (*pphead)->pre = NULL;//构建一个双向循环链表
//}

ListNode* Init()//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
{
	//自己需要创建一个哨兵位
	ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
	assert(phead);//可能开辟失败
	phead->data = -1;
	phead->pre = phead->next = phead;//记住这里要让他变成双向循环链表
	return phead;
}
ListNode* ListBuyNode(x)//创建结点
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	//来到这,说明空间开辟成功
	node->data = x;
	node->next = node->pre = NULL;
	return node;
}
void Print(ListNode* phead)//链表打印
{
	assert(phead);
	ListNode* pcur = phead->next;//定义一个用来 遍历的指针,注意他初始值是 phead->next
	while (pcur != phead)
	{
		printf("%d-> ", pcur->data);
		pcur = pcur->next;//记得更新
	}
	printf("\n");
}

void PushBack(ListNode* phead,DataType x)//尾插
{
	assert(phead);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//先处理node 的前驱,后继
	node->pre = phead->pre; //  phead->pre->next原来尾结点的后继,node->pre = phead->pre->next这里出现了野指针的问题 phead->pre->next没有具体指向
	node->next = phead;
	//接下来在处理 原来尾结点,哨兵位
	/*,这样写是错的,以下2句话不可以颠倒
	已经把node这个结点给覆盖掉了,在执行65行代码时,node 是未知的,因为这个结点已经被覆盖了,phead->pre->next此时他的指向也就是未知的,调试时不会报错,但是遍历读取他就会报错了,就像你在打印函数里进行打印时,出现野指针
	phead->pre = node;
	phead->pre->next = node;*/

	phead->pre->next = node;
	phead->pre = node;//别忘了,每尾插进来的结点最终都是一个新的尾结点
}
void PushFront(ListNode* phead,DataType x)//头插
{
	//头插是在哨兵位的后面进行插入
	assert(phead);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//处理node 的后继,前驱
	node->pre = phead;
	node->next = phead->next;
	//处理phead ,phead->next 
	/*这样写也是对的
	phead->next->pre = node;*/
	phead->next = node;//头节点要进行更新
	phead->next->pre = node;

}
void PopBack(ListNode* phead)//尾删
{
	//注意不能直接删除,需要先找到尾结点的前一个结点
	assert(phead);
	//先判断以下是否为空,有2种写法
	/*if (phead->pre == phead)
	{
		return 9;
	}*/
	assert(phead->next != phead);//是否为空
	ListNode* del = phead->pre;//把要删除的结点先保存起来
	//删除之前,先要构成一个新的双向循环链表
	del->pre->next = phead;
	phead->pre = del->pre;
	/*错误的,顺序不能颠倒,因为此时 del->pre已经被覆盖了
	phead->pre = del->pre;//新的尾结点
	del->pre->next = phead;*/

	free(del);
	del = NULL;

}
void PopFront(ListNode* phead)//头删
{
	assert(phead);
	assert(phead->next != phead);//确保不为空
	ListNode* del = phead->next;
	//以下2句没有先后顺序之分
	del->next->pre = phead;

	phead->next = del->next;
	free(del);
	del = NULL;

}
void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{
	assert(pos);
	//为插入的数据开辟空间
	ListNode* node = ListBuyNode(x);
	//处理 node 的前驱,后继
	node->next = pos->next;
	node->pre = pos;
	//处理 pos 和 pos->next的pre
	pos->next = node;
	pos->next->pre = node;

}
void Earse(ListNode* pos)//指定位置删除
{
	assert(pos);
	//需要找到pos之前的一个结点和之后的一个结点
	pos->next->pre = pos->pre;
	pos->pre->next = pos->next;
	free(pos);
	pos = NULL;
}
ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{
	assert(phead);
	ListNode* pcur = phead->next;//定义一个用来循环遍历的指针
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;//直接返回对应结点
		}
		pcur = pcur->next;
	}
	printf("没有找到\n");
}
void Destroy(ListNode* phead)//链表销毁;想一下,传一级指针还是二级?  在这里我们传入一级指针,为了保持接口一致性
{
	//销毁我们是一个一个进行删除,自然就需要遍历
	assert(phead);
	ListNode* del = phead->next;
	while (del != phead)
	{
		ListNode* next = del->next;
		free(del);
		/*del = NULL;*/    //  ?
		del = next;
	}
	//来到这说明,此时只有一个哨兵位
	free(phead);
	phead = NULL;

}
void LTDestroy(ListNode** pphead) 
{
		assert(pphead && *pphead);
		ListNode* cur = (*pphead)->next;
		while ( cur!=*pphead )
		{
			ListNode* next = cur->next;
			free(cur);
			cur = next;
		}
		free(*pphead);
		*pphead = NULL;
	}

 List.h对应完整代码

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>

#include<stdlib.h>
//双向链表的实现
typedef int DataType;
typedef struct ListNode {

	DataType data;//数据域
	struct ListNode* pre;//前驱域
	struct ListNode*next;//后继域
}ListNode;

//接口函数的声明
//void Init(ListNode** pphead);//初始化,采用传参的形式
ListNode* Init();//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
void Print(ListNode* phead);//链表打印
void PushBack(ListNode* phead,DataType x);//尾插,这里传入一级指针即可,因为返回的头节点我们不需要进行更改
void PushFront(ListNode* phead, DataType x);//头插
void PopBack(ListNode* phead);//尾删
void PopFront(ListNode* phead);//头删
void InsertAfter(ListNode* pos,DataType x);//指定位置之后插入
void Earse(ListNode* pos);//指定位置删除
ListNode* Find(ListNode*phead,DataType x);//按值查找,若是找到则返回对应的结点
void Destroy(ListNode* phead);//链表销毁;想一下,传一级指针还是二级?为了保持接口一致性我们传入一级指针


ok,以上就是我要为大家进行share的一些基本内容,都来到这里了,要是感觉我写的还不错的话,各位大佬烦劳点个赞,互关以下呗~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/107932.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis进军磁盘存储

目录 1、对抗价格优势&#xff1a;纳入磁盘&#xff0c;降低成本&#xff1f; 2、Redis的野心&#xff1a;无敌是多么寂寞&#xff0c;所以我们要开新地图 3、开发者异议&#xff1a;他们正在偏离我们选择Redis的初衷 4、结语&#xff1a;性能为王&#xff0c;但绝不甘于只…

Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (四)

这篇博客是之前文章&#xff1a; Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;一&#xff09;Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;二&a…

ROS自学笔记十八:ModuleNotFoundError: No module named ‘serial‘

出现上述错误&#xff0c;则需要安装serial功能包 第一步&#xff1a;输入 sudo apt install python3-pip 第二步&#xff1a;输入 pip install pyserial

List 3.5 详解原码、反码、补码

前言 欢迎来到我的博客&#xff0c;我是雨空集&#xff08;全网同名&#xff09;&#xff0c;无论你是无意中发现我&#xff0c;还是有意搜索而来&#xff0c;我都感到荣幸。这里是一个分享知识、交流想法的平台&#xff0c;我希望我的博客能给你带来帮助和启发。如果你喜欢我…

记一次线程爆满导致服务器崩溃的问题排查

记一次线程爆满导致服务器崩溃的问题排查 重启服务器 重启后&#xff0c;ssh连接发现下面问题 fork faild:Cannot allocate memory 以为是内存满了 于是&#xff0c;free -h,查看内存情况&#xff0c;还有&#xff0c;观察一段时间后&#xff0c;内存没多大变化 修改…

【扩散模型】HuggingFace Diffusers实战

HuggingFace Diffusers实战 1. 环境准备2. DreamBooth2.1 Stable Diffusion简介2.2 DreamBooth 3. Diffusers核心API4. 实战&#xff1a;生成美丽的蝴蝶图像4.1 下载数据集4.2 调度器4.3 定义扩散模型4.4 创建扩散模型训练循环4.5 图像的生成方法1.建立一个管线方法2.写一个采样…

MySQL 字符集与乱码与collation设置的问题?

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友…

线扫相机DALSA--常见问题一:软件安装顺序

1.软件安装顺序 先安装&#xff1a;Sapera_LT_SDK&#xff0c;后安装Xtium-CL MX4驱动。 2.初次安装CamExpert&#xff0c;重启电脑后未找到相机 Settings(搜索协议)配置完毕后&#xff0c;需点击Detect Camera(一键查找相机)按钮&#xff0c;搜索相机。第一次查找相机耗时会略…

word行内插入mathtype 公式后行距变大解决办法

现象 word行内插入mathtype 公式后行距变大 解决方法 选中要进行操作的那些行&#xff0c;依次单击菜单命令“格式→段落”&#xff0c;打开“段落”对话框&#xff1b;单击“缩进和间距”选项卡&#xff0c;将间距的“段前”和“段后”都调整为“0行”&#xff1b;将“如果…

【广州华锐互动】城市水处理VR仿真实训平台

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为我们带来了前所未有的沉浸式体验。在教育领域&#xff0c;VR技术的应用也日益广泛&#xff0c;为传统的教学模式带来了革命性的变革。本文将以城市水处理VR仿真实训…

Java集合类--List集合,Set集合,Map集合

集合可以看作一个容器&#xff0c;Java中提供了不同的集合类&#xff0c;这些类具有不同的存储对象的方式&#xff0c;同时提供了相应的方法&#xff0c;以便用户对集合进行遍历、添加、删除、查找指定的对象。 1.集合类概述&#xff1a; 集合类类似于数组&#xff0c;与数组不…

公网远程访问macOS本地web服务器

# 公网访问macOS本地web服务器【内网穿透】 文章目录 1. 启动Apache服务器2. 公网访问本地web服务2.1 本地安装配置cpolar2.2 创建隧道2.3 测试访问公网地址3. 配置固定二级子域名3.1 保留一个二级子域名3.2 配置二级子域名4. 测试访问公网固定二级子域名 以macOS自带的Apache…

局域网内两台电脑共享文件夹(通过网线直连共享数据)

文章目录 2.设置共享文件夹3.访问共享文件夹 1.将两台电脑置于同一局域网下 用网线将两台电脑连接关闭两台电脑防火墙将两台电脑IP地址设置在同一局域网下 测试是否在同一局域网下&#xff0c;使用ping命令 ping 192.168.0.122.设置共享文件夹 选择想要共享的文件夹&#xff…

基于单片机的智能电子鼻的设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、智能电子鼻系统的设计方案1.1智能电子鼻系统的设计思路1.2智能电子鼻系统的设计流程图1.3智能电子鼻系统的硬件数…

Android系统的特性

目录 Android系统的特性 1. 显示布局 2. 数据存储 3. 网络 4. 信息 5. 浏览器 6. 编程语言支持 7. 媒体支持 8. 流媒体支持 9. 硬件支持 10. 多点触控 11.蓝牙 12. 多任务处理 13. 语音功能 14.无线共享功能 15. 截图功能 16. 跨平台 17. 应用程序的安全机制…

IT行业变成了夕阳行业

IT技术发展背景及历程 从2010年左右开始&#xff0c;大众创新&#xff0c;万众创业变成了一个经常看到的词语&#xff0c;在创业潮的带动下&#xff0c;同时刚好赶上了互联网的高速发展&#xff0c;一大批互联网创业公司应运而生&#xff0c;在这样的背景下&#xff0c;IT行业…

JVM进阶(3)

一)什么是垃圾&#xff1f; 垃圾指的是在应用程序中没有任何指针指向的对象&#xff0c;这个对象就是需要被回收的垃圾&#xff0c;如果不及时的针对内存中的垃圾进行清理&#xff0c;那么这些垃圾对象所占用的内存空间可能一直保留到应用程序结束&#xff0c;被保留的空间无法…

常见排序算法之冒泡排序

冒泡排序&#xff0c;英文名Bubble Sort&#xff0c;是一种相对基础的 交换排序方法。这种排序算法的名字来源于它操作的过程&#xff0c;可以类比为数列中的每一个元素都可以像小气泡一样&#xff0c;根据自身的大小一点一点向数组的一侧移动。具体到冒泡排序的工作原理&#…

即时编译器JIT

类编译加载执行过程 如下图所示&#xff0c;一个Java代码从编译到运行大抵会经历以下几个过程。具体每个过程笔者会在下文站展开讨论。 类编译 首先是类编译阶段&#xff0c;这个阶段会将Java文件变为class文件&#xff0c;这个class文件包含一个常量池和方法表集合&#xf…

Android数据对象序列化原理与应用

序列化与反序列化 序列化是将对象转换为可以存储或传输的格式的过程。在计算机科学中&#xff0c;对象通常是指内存中的数据结构&#xff0c;如数组、列表、字典等。通过序列化&#xff0c;可以将这些对象转换为字节流或文本格式&#xff0c;以便在不同的系统之间进行传输或存…