线性集合: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
共同点:都是线性集合 ArrayList ArrayList 底层是基于数组实现的,并且实现了动态扩容(当需要添加新元素时,如果 elementData 数组已满,则会自动扩容,新的容量将是原来的 1.5 倍),来…...

llama3 发布!大语言模型新选择 | 开源日报 No.251
meta-llama/llama Stars: 53.0k License: NOASSERTION llama 是用于 Llama 模型推理的代码。 提供了预训练和微调的 Llama 语言模型,参数范围从 7B 到 70B。可以通过下载脚本获取模型权重和 tokenizer。支持在本地快速运行推理,并提供不同规格的模型并…...

SpringBoot 具体是做什么的?
Spring Boot是一个用于构建独立的、生产级别的、基于Spring框架的应用程序的开源框架。它的目标是简化Spring应用程序的开发和部署过程,通过提供一种快速、便捷的方式来创建Spring应用程序,同时保持Spring的灵活性和强大特性。 1. 简化Spring应用程序开…...
Debian常用命令
Debian是一个开源的Unix-like操作系统,提供了大量的软件包供用户安装和使用。在Debian系统中,命令行界面(CLI)是用户与系统进行交互的重要工具。以下是Debian中一些常用的命令及其详细解释: 文件和目录操作命令&#x…...
常见的前端框架
常用的前端框架有以下几种: 模型 React:由Facebook开发的一款前端框架,采用虚拟DOM的概念,可高效地更新页面。Vue.js:一款轻量级的前端框架,易学易用,支持组件化开发和双向数据绑定。AngularJ…...

初学者如何选择ARM开发硬件?
在开始前我有一些资料,是我根据网友给的问题精心整理了一份「ARM的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!如果你没有ARM开发经验࿰…...

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

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

【LeetCode】每日一题 2024_5_13 腐烂的橘子(经典多源 BFS)
文章目录 LeetCode?启动!!!题目:找出不同元素数目差数组题目描述代码与解题思路 每天进步一点点 LeetCode?启动!!! 好久没写每日一题题解了,今天重新起航 干…...

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

【网络安全入门】你必须要有的学习工具(附安装包)零基础入门到进阶,看这一篇就够了!
工欲善其事必先利其器 在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧! Web安全类 Web类工具主要是通过各种扫描工具,发现web站点存在的各种漏洞如sql注入、xss等。从而获取系统权限,常用的…...

【解决】:git clone项目报错fatal: fetch-pack: invalid index-pack output
象:之前一直使用gitee将个人学习和工作相关记录上传到个人gitee仓库,一直没出现过问题。直到有一天换电脑重新拉取代码发现出了问题,具体如下图: 原因分析: 经过查询发现主要原因是因为git clone的远程仓库的项目过大…...

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

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

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

清华团队开发首个AI医院小镇模拟系统;阿里云发布通义千问 2.5:超越GPT-4能力;Mistral AI估值飙升至60亿美元
🦉 AI新闻 🚀 清华团队开发首个AI医院小镇模拟系统 摘要:来自清华的研究团队最近开发出了一种创新的模拟系统,名为"Agent Hospital",该系统能够完全模拟医患看病的全流程,其中包括分诊、挂号、…...
React Suspense与Concurrent Mode:探索异步渲染的新范式
React的Suspense和Concurrent Mode是两个强大的特性,它们共同改变了React应用处理异步数据加载和UI渲染的方式。下面我将通过一个简化的代码示例来展示如何使用这两个特性。 Concurrent Mode 和 Suspense 的基本用法 首先,确保你使用的是支持这些特性的…...
算法训练营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】)
帧动画 帧动画也叫序列帧动画,其原理就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。ArkUI开发框架提供了 ImageAnimator 组件实现帧动画能力,本节笔者介绍一下 ImageAnimator 组件的简单使用。 官方文献 说明 该组件从A…...
vant NavBar 导航栏详解
vant 是一个基于 Vue 的移动端 UI 组件库,而 NavBar 是其中的一个导航栏组件。下面是对 vant 的 NavBar 导航栏组件的详细解释: 1. 引入 NavBar 首先,你需要在你的 Vue 组件中引入 NavBar 组件: import { NavBar } from vant; …...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...