当前位置: 首页 > news >正文

线性集合:ArrayList,LinkedList,Vector/Stack

共同点:都是线性集合

ArrayList

ArrayList 底层是基于数组实现的,并且实现了动态扩容(当需要添加新元素时,如果 elementData 数组已满,则会自动扩容,新的容量将是原来的 1.5 倍),来看一下 ArrayList 的部分源码(PS:以下代码均来自Java8,不同版本可能存在细微差距)。

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10; // 默认容量private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData; // 存储元素的数组,数组类型:Objectprivate int size; // 列表的大小,即列表中元素的个数

ArrayList 还实现了 RandomAccess 接口,这是一个标记接口

public interface RandomAccess {
}

内部是空的,标记“实现了这个接口的类支持快速(通常是固定时间)随机访问”。快速随机访问是什么意思呢?就是说不需要遍历,就可以通过下标(索引)直接访问到内存地址。而 LinkedList 没有实现该接口,表示它不支持高效的随机访问,需要通过遍历来访问元素。

ArrayList 还实现了 Cloneable 接口,并且重写了 Object 类的 clone() 方法,但只是浅拷贝,还是要根据需求使用。

public Object clone() {try {ArrayList<?> v = (ArrayList<?>) super.clone();v.elementData = Arrays.copyOf(elementData, size);v.modCount = 0;return v;} catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError(e);}
}

ArrayList 还实现了 Serializable 接口,支持序列化:

但是关键字段 elementData 使用了 transient 关键字修饰,这个关键字的作用是,让它修饰的字段不被序列化。

看到这里是不是心里出现了很多问好?

我们这样来看:elementData 是一个数组,数组是定长的,如果一个新创建的ArrayList,并且我们只往里添加了2个元素,如果我们默认序列化就会多序列化8个空的内存空间,我们再反序列化出来的时候需要更大的空间去接收这个数组。如下例子中可以更好的反应该问题,可能出现很大的bug:

public class Main {public static void main(String[] args) throws Exception {List<Integer> list = new ArrayList<>();for (int i = 0; i < 100000; i++) {list.add(i);}System.out.println(list.size());list.clear();Class<? extends List> listClass = list.getClass();Field field = listClass.getDeclaredField("elementData");field.setAccessible(true);Object[] o = (Object[]) field.get(list);System.out.println(o.length);}
}输出如下:
100000
106710

 于是,ArrayList 做了一个愉快而又聪明的决定,内部提供了两个私有方法 writeObject 和 readObject 来完成序列化和反序列化。

private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException{// Write out element count, and any hidden stuffint expectedModCount = modCount;s.defaultWriteObject();// Write out size as capacity for behavioural compatibility with clone()s.writeInt(size);// Write out all elements in the proper order.for (int i=0; i<size; i++) {s.writeObject(elementData[i]);}if (modCount != expectedModCount) {throw new ConcurrentModificationException();}
}private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {elementData = EMPTY_ELEMENTDATA;// Read in size, and any hidden stuffs.defaultReadObject();// Read in capacitys.readInt(); // ignoredif (size > 0) {// be like clone(), allocate array based upon size not capacityint capacity = calculateCapacity(elementData, size);SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);ensureCapacityInternal(size);Object[] a = elementData;// Read in all elements in the proper order.for (int i=0; i<size; i++) {a[i] = s.readObject();}}
}

从源码中可以看出序列化和反序列化时,只保存了list的大小和所有元素。

还需要注意 ArrayList 在序列化时,不允许有并发的修改操作。

Vector/Stack

Vector 也是基于数组实现的,但是是线程安全的,其他和 ArrayList 基本没有区别,源码注释中有句话也可以看出

{@code Vector} is synchronized.  If a thread-safe
implementation is not needed, it is recommended to use {@link
ArrayList} in place of {@code Vector}.// Vector的序列化和反序列化的方法与ArrayList略有差异
private void readObject(ObjectInputStream in)throws IOException, ClassNotFoundException {ObjectInputStream.GetField gfields = in.readFields();int count = gfields.get("elementCount", 0);Object[] data = (Object[])gfields.get("elementData", null);if (count < 0 || data == null || count > data.length) {throw new StreamCorruptedException("Inconsistent vector internals");}elementCount = count;elementData = data.clone();
}private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {final java.io.ObjectOutputStream.PutField fields = s.putFields();final Object[] data;synchronized (this) {fields.put("capacityIncrement", capacityIncrement);fields.put("elementCount", elementCount);data = elementData.clone();}fields.put("elementData", data);s.writeFields();
}

 Stack 继承了 Vector,同时Stack添加了 push/pop/peek 等方法,实现了后进先出(LIFO)。

LinkedList

LinkedList 是一个继承自 AbstractSequentialList 的双向链表,同时实现了 Deque 双向队列接口,因此它也可以被当作堆栈、队列或双向队列进行操作。

 部分源码

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{transient int size = 0; // 表示链表中的节点个数transient LinkedList.Node<E> first; // 链表中的第一个节点transient LinkedList.Node<E> last; // 链表中的最后一个节点

可以看到 LinkedList 同样实现了 Serializable 接口,支持序列化。但是上面源码中 LinkedList 的所有属性都是 transient 修饰的,这又让我们想到了 ArrayList 的序列化实现,果然找到了writeObject和readObject方法的实现:

private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out any hidden serialization magics.defaultWriteObject();// Write out sizes.writeInt(size);// Write out all elements in the proper order.for (LinkedList.Node<E> x = first; x != null; x = x.next)s.writeObject(x.item);
}@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject();// Read in sizeint size = s.readInt();// Read in all elements in the proper order.for (int i = 0; i < size; i++)linkLast((E)s.readObject());
}

仔细琢磨,发现不仅尽可能少的占用存储空间,反序列化时还巧妙的恢复了原来的顺序。

相关文章:

线性集合:ArrayList,LinkedList,Vector/Stack

共同点&#xff1a;都是线性集合 ArrayList ArrayList 底层是基于数组实现的&#xff0c;并且实现了动态扩容&#xff08;当需要添加新元素时&#xff0c;如果 elementData 数组已满&#xff0c;则会自动扩容&#xff0c;新的容量将是原来的 1.5 倍&#xff09;&#xff0c;来…...

llama3 发布!大语言模型新选择 | 开源日报 No.251

meta-llama/llama Stars: 53.0k License: NOASSERTION llama 是用于 Llama 模型推理的代码。 提供了预训练和微调的 Llama 语言模型&#xff0c;参数范围从 7B 到 70B。可以通过下载脚本获取模型权重和 tokenizer。支持在本地快速运行推理&#xff0c;并提供不同规格的模型并…...

SpringBoot 具体是做什么的?

Spring Boot是一个用于构建独立的、生产级别的、基于Spring框架的应用程序的开源框架。它的目标是简化Spring应用程序的开发和部署过程&#xff0c;通过提供一种快速、便捷的方式来创建Spring应用程序&#xff0c;同时保持Spring的灵活性和强大特性。 1. 简化Spring应用程序开…...

Debian常用命令

Debian是一个开源的Unix-like操作系统&#xff0c;提供了大量的软件包供用户安装和使用。在Debian系统中&#xff0c;命令行界面&#xff08;CLI&#xff09;是用户与系统进行交互的重要工具。以下是Debian中一些常用的命令及其详细解释&#xff1a; 文件和目录操作命令&#x…...

常见的前端框架

常用的前端框架有以下几种&#xff1a; 模型 React&#xff1a;由Facebook开发的一款前端框架&#xff0c;采用虚拟DOM的概念&#xff0c;可高效地更新页面。Vue.js&#xff1a;一款轻量级的前端框架&#xff0c;易学易用&#xff0c;支持组件化开发和双向数据绑定。AngularJ…...

初学者如何选择ARM开发硬件?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「ARM的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;如果你没有ARM开发经验&#xff0…...

Mysql 多表查询,内外连接

内连接&#xff1a; 隐式内连接 使用sql语句直接进行多表查询 select 字段列表 from 表1 , 表2 where 条件 … ; 显式内连接 将‘&#xff0c;’改为 inner join 连接两个表的 on select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 … ; select emp.id, emp.name, …...

【C语言】函数

目录 一、函数的概念 二、库函数 2.1 ❥ 标准库 2.2 ❥ 库函数的使用方法 三、自定义函数 四、形参和实参 4.1 ❥ 实参&#xff08;实际参数&#xff09; 4.2 ❥ 形参&#xff08;形式参数&#xff09; 五、return语句 六、函数的调用 6.1 ❥ 传值调用 6.2 ❥ 传址调…...

【LeetCode】每日一题 2024_5_13 腐烂的橘子(经典多源 BFS)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;找出不同元素数目差数组题目描述代码与解题思路 每天进步一点点 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 好久没写每日一题题解了&#xff0c;今天重新起航 干…...

【Linux系统编程】第十七弹---进程理解

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、进程的基本概念 2、描述进程-PCB 2.1、什么是PCB 2.2、为什么要有PCB 3、task_ struct 3.1、启动进程 3.2、创建进程…...

【网络安全入门】你必须要有的学习工具(附安装包)零基础入门到进阶,看这一篇就够了!

工欲善其事必先利其器 在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧&#xff01; Web安全类 Web类工具主要是通过各种扫描工具&#xff0c;发现web站点存在的各种漏洞如sql注入、xss等。从而获取系统权限&#xff0c;常用的…...

【解决】:git clone项目报错fatal: fetch-pack: invalid index-pack output

象&#xff1a;之前一直使用gitee将个人学习和工作相关记录上传到个人gitee仓库&#xff0c;一直没出现过问题。直到有一天换电脑重新拉取代码发现出了问题&#xff0c;具体如下图&#xff1a; 原因分析&#xff1a; 经过查询发现主要原因是因为git clone的远程仓库的项目过大…...

python随机显示四级词汇

python实现一个浮动窗口随机显示四级单词在桌面跑来跑去 实现一个浮动窗体随机显示四级单词在windows桌面置顶移动 tkinter库来创建窗口和显示单词&#xff0c;以及random库来随机选择单词。 使用after方法来定时更新窗口的位置&#xff0c;实现单词窗口的慢慢移动效果 使用…...

vuerouter声明式导航

声明式导航-跳转传参数 1.查询参数传参 语法&#xff1a;to /path?参数名值 2.对应页面组件接受传来的值 $router.query.参数名 2.动态路由传参 1.配置动态路由 2.配置导航连接 to/path/参数值 3.对应页面组件接收传递过来的值 #route.params.参数名 多个参数传递&…...

视频断点上传

什么是断点续传 通常视频文件都比较大&#xff0c;所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制&#xff0c;但是客户的网络环境质量、电脑硬件环境等参差不齐&#xff0c;如果一个大文件快上传完了网断了没有上传完成&#xf…...

清华团队开发首个AI医院小镇模拟系统;阿里云发布通义千问 2.5:超越GPT-4能力;Mistral AI估值飙升至60亿美元

&#x1f989; AI新闻 &#x1f680; 清华团队开发首个AI医院小镇模拟系统 摘要&#xff1a;来自清华的研究团队最近开发出了一种创新的模拟系统&#xff0c;名为"Agent Hospital"&#xff0c;该系统能够完全模拟医患看病的全流程&#xff0c;其中包括分诊、挂号、…...

React Suspense与Concurrent Mode:探索异步渲染的新范式

React的Suspense和Concurrent Mode是两个强大的特性&#xff0c;它们共同改变了React应用处理异步数据加载和UI渲染的方式。下面我将通过一个简化的代码示例来展示如何使用这两个特性。 Concurrent Mode 和 Suspense 的基本用法 首先&#xff0c;确保你使用的是支持这些特性的…...

算法训练营day37

动态规划 1.斐波那契数 1.使用数组存储子问题结果 class Solution {public int fib(int N) {if (N 0) return 0;int[] dp new int[N 1];// base casedp[0] 0; dp[1] 1;// 状态转移for (int i 2; i < N; i) {dp[i] dp[i - 1] dp[i - 2];}return dp[N];} }2.使用变…...

基础ArkTS组件:帧动画,内置动画组件,跑马灯组件(HarmonyOS学习第三课【3.6】)

帧动画 帧动画也叫序列帧动画&#xff0c;其原理就是在时间轴的每帧上逐帧绘制不同的内容&#xff0c;使其连续播放而成动画。ArkUI开发框架提供了 ImageAnimator 组件实现帧动画能力&#xff0c;本节笔者介绍一下 ImageAnimator 组件的简单使用。 官方文献 说明 该组件从A…...

vant NavBar 导航栏详解

vant 是一个基于 Vue 的移动端 UI 组件库&#xff0c;而 NavBar 是其中的一个导航栏组件。下面是对 vant 的 NavBar 导航栏组件的详细解释&#xff1a; 1. 引入 NavBar 首先&#xff0c;你需要在你的 Vue 组件中引入 NavBar 组件&#xff1a; import { NavBar } from vant; …...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...