ArrayList部分底层源码分析

JDK版本为1.8.0_271,以插入和删除元素为例,部分源码如下:

// 部分属性
transient Object[] elementData;	// 底层数组
private int size;	// 记录元素个数
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};	// 空Object数组//构造器
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  //初始化为空数组
}//方法:add()相关方法
public boolean add(E e) {//查看当前数组是否够多存一个元素ensureCapacityInternal(size + 1);  // Increments modCount!!//存入新元素到[size]位置,然后size自增1elementData[size++] = e;return true;
}// 根据当前插入元素后所需要的容量判断
private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}// 计算当前插入元素后所需要的容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {//如果当前数组还是空数组if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//那么minCapacity取DEFAULT_CAPACITY(10)与minCapacity的最大值return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}//查看是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {modCount++;  //修改次数加1//如果需要的最小容量比当前数组的长度大,即当前数组不够存,就扩容if (minCapacity - elementData.length > 0)grow(minCapacity);
}// 数组扩容,在默认扩容为1.5倍,不够的话就选minCapacity,并校验是否越界,然后拷贝元素
private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length; //当前数组容量int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组容量是旧数组容量的1.5倍//看旧数组的1.5倍是否够if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//看新数组长度是否超过最大数组限制(Integer.MAX_VALUE-8)if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);//复制一个新数组elementData = Arrays.copyOf(elementData, newCapacity);
}// 新数组长度超过最大数组限制(Integer.MAX_VALUE-8)
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();// 对minCapacity和MAX_ARRAY_SIZE进行比较// 若minCapacity大,将Integer.MAX_VALUE作为新数组的大小// 若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}// 移除元素,elementData为底层数组
public boolean remove(Object o) {// 判断当前元素是否为nullif (o == null) {	// 逐个遍历底层数组中元素,for (int index = 0; index < size; index++)// 是否存在nullif (elementData[index] == null) {fastRemove(index);	// 移除index位置的元素return true;}} else {// 逐个遍历底层数组中元素,for (int index = 0; index < size; index++)// 是否存在与o相等的元素if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;
}// 移除index位置的元素
private void fastRemove(int index) {modCount++;	// 修改次数加1int numMoved = size - index - 1;	// 需要迁移的元素数量,[index+1,size)区间// 根据numMoved判断是否需要迁移index之后的元素if (numMoved > 0)	// 不是,需要迁移元素// 迁移[index+1,size)区间到[index,size-1)区间System.arraycopy(elementData, index+1, elementData, index,numMoved);// 更新sizeelementData[--size] = null; 
}

在网上找了个添加元素的示意图,如图所示:

  • ArrayList采用Object[]数组作为底层实现

在这里插入图片描述

  • ArrayList自动扩容过程
    grow扩容方法会传入minCapacity,表示新list所需的最小容量;
    新list的容量newCapacity取原list长度*1.5和minCapacity的较大值在这里插入图片描述

  • ArrayList的add(E e)方法
    直接在list尾部插入元素e,无需迁移元素,时间复杂度o(1)

  • ArrayList的add(int index,E e)方法
    在index位置插入元素e前,需要把之前[index,size)的元素整体向右迁移一个单位;
    然后插入元素e到index位置,并更新size,时间复杂度o(n)

在这里插入图片描述

ArrayList与Vector的区别

它们的底层物理结构都是数组,我们称为动态数组。

  • ArrayList是新版的动态数组,线程不安全,效率高,Vector是旧版的动态数组,线程安全,效率低。
  • 动态数组的扩容机制不同,ArrayList默认扩容为原来的1.5倍,Vector默认扩容增加为原来的2倍
  • 数组的初始化容量,如果在构建ArrayList与Vector的集合对象时,没有显式指定初始化容量,那么Vector的内部数组的初始容量默认为10,而ArrayList在JDK 7.0 及之前的版本也是10(饿汉式),JDK8.0 之后的版本ArrayList初始化为长度为0的空数组,之后在添加第一个元素时,再创建长度为10的数组。(懒汉式)
  • 原因: ArrayList添加元素的时候,再创建数组,避免浪费。因为很多方法的返回值是ArrayList类型,需要返回一个ArrayList的对象,例如:后期从数据库查询对象的方法,返回值很多就是ArrayList。有可能你要查询的数据不存在,要么返回null,要么返回一个没有元素的ArrayList对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网实战--驱动篇之(五)TEA和AES加密算法

目录 一、前言 二、TEA算法 三、AES算法 四、加解密测试 五、安全性保障 一、前言 物联网的安全性是经常被提及的一个点&#xff0c;如果你的设备之间通讯没有加密的话&#xff0c;那么攻击者很容易就能获取并解析出报文的协议&#xff0c;从而根据攻击者的需要进行设备操…

MongoDB的安装和使用

1.MongoDB 安装 1.1 基于Docker安装 docker run --restartalways -d --name mongo -v /opt/mongodb/data:/data/db -p 27017:27017 mongo:4.0.6 1.2 客户端工具使用 MongoDB Compass | MongoDB 2.MongoDB 使用 2.1 引用依赖包 <dependency><groupId>org.sprin…

信号完整性的常见术语概念(面试常用)

目录 术语 概念一览 1&#xff0e;信号完整性&#xff08;Signal Integrity&#xff09; 2&#xff0e;传输线&#xff08;Transmission Line&#xff09; 3&#xff0e;特性阻抗&#xff08;Characteristic Impedance&#xff09; 4&#xff0e;反射&#xff08;Reflecti…

【环境搭建】ubuntu工作站搭建全流程(显卡4090)

安装ubuntu22.04系统 首先&#xff0c;先压缩windows分区&#xff0c;按住Win X快捷键&#xff0c;选择磁盘管理,压缩分区&#xff0c;压缩出新的分区用于安装ubuntu22.04 windows插入系统盘&#xff0c;点击重启&#xff0c;一直按F12,选择系统盘启动方式语言选择chinese–…

[react优化] 避免组件或数据多次渲染/计算

代码如下 点击视图x➕1,导致视图更新, 视图更细导致a也重新大量计算!!这很浪费时间 function App() {const [x, setX] useState(3)const y x 2console.log(重新渲染, x, y);console.time(timer)let a 0for (let index 0; index < 1000000000; index) {a}console.timeE…

用ansys q3d提取pcb板上的寄生参数及注意事项

需求 画好pcb板后&#xff0c;想要提取回路的寄生参数 1 保存为ad格式 因为之前图方便用立创eda画的板子&#xff0c;结果无法导出成想要的格式。因此需要将立创eda的文件导出为ad格式。立创eda的官网有相关教程。 注意事项&#xff08;只说自己遇到的问题&#xff09; 导…

Ubuntu22.04 + ROS2 Humble的环境配置

Ubuntu22.04 ROS2 Humble的环境配置 文章目录 Ubuntu22.04 ROS2 Humble的环境配置(1) Set locale(2) Setup Sources(3)安装ROS2(4)检查是否成功安装 参考官方网站ROS2-Installation ROS2的各种版本及维护计划&#xff0c;可以参考ROS2-List of Distributions (1) Set locale…

Django中的静态文件、路径、访问静态文件的方法

1.什么是静态文件 不能与服务器端做动态交互的文件都是静态文件 如:图片,css,js,音频,视频,html文件(部分) 2.静态文件配置 在 settings.py 中配置一下两项内容: 1.配置静态文件的访问路径 通过哪个url地址找静态文件 STATIC_URL ‘/static/’ 说…

独一无二:探索单例模式在现代编程中的奥秘与实践

设计模式在软件开发中扮演着至关重要的角色&#xff0c;它们是解决特定问题的经典方法。在众多设计模式中&#xff0c;单例模式因其独特的应用场景和简洁的实现而广受欢迎。本文将从多个角度详细介绍单例模式&#xff0c;帮助你理解它的定义、实现、应用以及潜在的限制。 1. 什…

数据结构篇1—《顺序表》

文章目录 &#x1f6a9;前言1. 数据结构的概念2. 数据结构的分类3. 顺序表3.1. 顺序表的分类&#xff08;1&#xff09;静态顺序表&#xff08;2&#xff09;动态顺序表 4. 动态顺序表实现4.1. 实现步骤&#xff08;1&#xff09;框架结构&#xff08;2&#xff09;SeqList.h头…

如何准备2024年汉字小达人:18道历年考题示例和解析、备考提醒

现在距离2024年第11届汉字小达人比赛还有六个多月的时间&#xff0c;如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。阅读理解不需要。②把历年真题刷刷熟&#x…

linux如何使 CPU使用率保持在指定百分比?

目录 方法1&#xff1a;&#xff08;固定在100%&#xff09; 方法2&#xff1a;&#xff08;可以指定0~100%&#xff09; 方法3&#xff1a;使用ChaosBlade工具&#xff08;0~100%&#xff09; 方法1&#xff1a;&#xff08;固定在100%&#xff09; for i in seq 1 $(cat /pro…

vscode常用插件

1. chinese&#xff08;汉化编译器&#xff09; chinese插件适用于 VS Code 的中文&#xff08;简体&#xff09;语言包&#xff0c;此中文&#xff08;简体&#xff09;语言包为 VS Code 提供本地化界面。 2、vetur&#xff08;vue 2开发必备&#xff09;volar&#xff08;vu…

【Jenkins】Jenkins自动化工具介绍

目录 技术背景常规的手动打包步骤 Jenkins简介起源与发展Jenkins的核心价值1.自动化1.1代码构建1.2测试自动化1.3自动部署 2.持续集成与持续部署CI/CD的概念如何减少集成问题更快速地发布软件版本 Jenkins优势Jenkins的主要竞争对手Travis CI:CircleCI:GitLab CI: Jenkins与其他…

SpringBoot之集成Redis

SpringBoot之集成Redis 一、Redis集成简介二、集成步骤2.1 添加依赖2.2 添加配置2.3 项目中使用 三、工具类封装四、序列化 &#xff08;正常都需要自定义序列化&#xff09;五、分布式锁&#xff08;一&#xff09;RedisTemplate 去实现场景一&#xff1a;单体应用场景二&…

vim卡死了,没有反应怎么办?

解决办法&#xff1a; 很有可能是你有个在window下的好习惯&#xff0c;没事儿就ctrl s保存文件。但是在vim里&#xff0c;ctrl s默认是发送一种流控制信号&#xff0c;通常用于停止终端的输出&#xff0c;所以你的屏幕就卡死了。 解决办法也很简单&#xff0c;按下ctrl q即…

NJU PA0

NJU PA0 使用教程提供的源再进行sudo apt install … 可能会出现 Unmet dependencies 此类报错 可以安装 aptitude sudo apt install aptitude sudo aptitude install <package>然后它会提示你&#xff0c;选 n 进行降级。再选 Y 确认 或者 将 /etc/apt/sources.list 下…

【c++】优先级队列|反向迭代器(vector|list)

优先级队列的常用函数的使用 #include<iostream> #include<queue> using namespace std;int main() {priority_queue<int>st;st.push(1);st.push(7);st.push(5);st.push(2);st.push(3);st.push(9);while (!st.empty()){cout << st.top() << &qu…

UDP简单总结

UDP&#xff1a;用户数据报协议 特点: 无连接、不可靠通信 不事先建立连接&#xff0c;数据按照包发&#xff0c;一包数据包含&#xff1a;自己的IP、程序端口、目的地IP、程序端口和数据(限制在64KB内) 发送方不管对方是否在线&#xff0c;数据在中间丢失也不管&#xff0c;…

SpringBoot与MyBatisPlus的依赖版本冲突问题

记录使用SpringBoot和MyBatisPlus时遇到的版本冲突问题解决。 java版本&#xff1a;jdk17 废话&#xff1a;&#xff09;目前在IDEA中使用Spring官方的脚手架最低jdk版本竟然是jdk17了。 当使用SpringBoot3.0版本(3.2.4)&#xff0c;配合使用MP3.5.2版本时报错&#xff1a; Er…