数据结构数组 Array 手写实现,扩容原理
数组数据结构
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型数据的集合。

数组的特点:
- 数组是相同数据类型的元素集合(int 不能存放 double)
- 数组中各元素的存储是有先后顺序的,它们在内存中按照这个顺序连续存放到一起。内存地址连续。
- 数组获取元素的时间复杂度为O(1)
1. 一维数组
维数组是最常用的数组,其他很多数据结构的变种也都是从一维数组来的。例如 HashMap 的拉链寻址结构,ThreadLocal 的开放寻址结构,都是从一维数组上实现的。
2. 二维数组

二维以及多维数组,在开发场景中使用到的到是不多,不过在一些算法逻辑,数学计算中到是可以使用。
✍️手写实现数组列表
在 Java 的源码中,数组是一个非常常用的数据结构,很多其他数据结构也都有数组的影子。在一些数据存放和使用的场景中,基本也都是使用 ArrayList 而不是 LinkedList
需要实现的方法
public interface PjpList<E> {// 添加boolean add(E e);// 删除E remove(int index);// 获取E get(int index);}
实现类
public class PjpArrayList<E> implements PjpList<E> {}
1. 📐基本设计
数组是一个固定的、连续的、线性的数据结构,那么想把它作为一个自动扩展容量的数组列表,则需要做一些扩展。
/*** 默认初始化空间*/
private static final int DEFAULT_CAPACITY = 10;/*** 空元素*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** ArrayList 元素数组缓存区*/
transient Object[] elementData;/*** List 集合元素数量*/
private int size;
2. 初始化
初始化分指定大小、不指定大小
public PjpArrayList() {// 初始化 ArrayList 阶段,如果不指定大小,默认给个空的元素,当开始添加元素的时候在初始化长度this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}public PjpArrayList(int initialCapacity) {
if (initialCapacity > 0) {// 如果给定长度大于0,那么直接创建一个数组this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
} else {throw new IllegalArgumentException("Illegal Capacity: " +initialCapacity);
}
}
- 初始化 ArrayList 阶段,如果不指定大小,默认会初始化一个空的元素。这个时候是没有默认长度的。
- 那么什么时候给初始化的长度呢?是在首次添加元素的时候,因为所有的添加元素操作,也都是需要判断容量,以及是否扩容的。那么在 add 添加元素时统一完成这个事情,还是比较好处理的。
- 之后就是随着元素的添加,容量是会不足的。当容量不足的是,需要进行扩容操作。同时还得需要把旧数据迁移到新的数组上。所以数据的迁移算是一个比较耗时的操作
3. 添加元素
@Override
public boolean add(E e) {
int minCapacity = size + 1;
// 判断当前容量与初始化容量,使用 Math.max 函数取最大值最为最小初始化空间
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
if (minCapacity - elementData.length > 0) {int oldCapacity = elementData.length;// 扩为原来的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 如果是第一次扩容,扩容至初始化空间大小if (newCapacity - minCapacity < 0) {newCapacity = minCapacity;}elementData = Arrays.copyOf(elementData, newCapacity);
}
elementData[size++] = e;
return true;
}
- 判断当前容量与初始化容量,使用 Math.max 函数取最大值最为最小初始化空间。
- 接下来是判断 minCapacity 和元素的数量,是否达到了扩容。首次创建 ArrayList 是一定会扩容的,也就是初始化 DEFAULT_CAPACITY = 10 的容量。
- Arrays.copyOf 实际上是创建一个新的空间数组,之后调用的 System.arraycopy 迁移到新创建的数组上。这样后续所有的扩容操作,也就都保持统一了。
- ArrayList 扩容完成后,就是使用 elementData[size++] = e; 添加元素操作了。
4. 移除元素
ArrayList 的重点离不开对 System.arraycopy 的使用,它是一个本地方法,可以让你从原数组的特定位置,迁移到新数组的指定位置和迁移数量。
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
arraycopy 各个参数的含义
src: 原数组
srcPos: 原数组起始位置(从这个位置开始复制)
dest: 目标数组
destPos:目标数组粘贴的起始位置
length: 复制的个数
元素删除
public E remove(int index) {
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0) {System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null; // 为了GC和我们不会再读到
return oldValue;
}

- ArrayList 的元素删除,就是在确定出元素位置后,使用 System.arraycopy 拷贝数据方式移动数据,把需要删除的元素位置覆盖掉。
- 此外它还会把已经删除的元素设置为 null 一方面让我们不会在读取到这个元素,另外一方面也是为了 GC
5. 获取元素
@Override
public E get(int index) {
return (E) elementData[index];
}@Override
public String toString() {return "PjpArrayList{" +"elementData=" + Arrays.toString(elementData) +", size=" + size +'}';
}
- 获取元素就比较简单了,直接从 elementData 使用索引直接获取即可。这个是一个 O(1) 操作。也正因为搜索元素的便捷性,才让 ArrayList 使用的那么广泛。
测试
public class test {public static void main(String[] args) {PjpList<String> num =new PjpArrayList<>();num.add("a");num.add("b");num.add("c");num.remove(0);System.out.println(num.get(1));System.out.println(num);num.add("d");num.add("e");num.add("f");num.add("g");num.add("h");num.add("i");num.add("j");num.add("k");num.add("l");System.out.println("扩容");System.out.println(num);}
}
测试结果
⚠️注意:在添加第 11 个元素的时候,数组进行了扩容长度扩为原来的 1.5 倍(10 ->15)
c
PjpArrayList{elementData=[b, c, null, null, null, null, null, null, null, null], size=2}
扩容
PjpArrayList{elementData=[b, c, d, e, f, g, h, i, j, k, l, null, null, null, null], size=11}相关文章:
数据结构数组 Array 手写实现,扩容原理
数组数据结构 数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型数据的集合。 数组的特点: 数组是相同数据类型的元素集合(int 不能存放 double)数组中各元素的存储是有先…...
工作中几个问题的思考
对于需要并行多公司并行处理的任务,方案是什么? 多线程、并行流、并发库(ExecutorService、Futrue、Callable),分布式计算(1)按照公司ID分片 (2)按照业务类型分片 处理…...
Jmeter的性能测试
性能测试的概念 定义:软件的性能是软件的一种非功能特性,它关注的不是软件是否能够完成特定的功能,而是在完成该功能时展示出来的及时性。 由定义可知性能关注的是软件的非功能特性,所以一般来说性能测试介入的时机是在功能测试…...
IntelliJ IDEA 2020.2.1白票安装使用方法
先安装好idear Plugins 内手动添加第三方插件仓库地址:https://plugins.zhile.io 搜索:IDE Eval Reset插件进行安装 输入https://plugins.zhile.io 手动安装离线插件方法 安装包可以去笔者的CSDN资源库下载 安装mybaties插件...
【UCAS自然语言处理作业一】利用BeautifulSoup爬取中英文数据,计算熵,验证齐夫定律
文章目录 前言中文数据爬取爬取界面爬取代码 数据清洗数据分析实验结果 英文数据爬取爬取界面动态爬取 数据清洗数据分析实验结果 结论 前言 本文分别针对中文,英文语料进行爬虫,并在两种语言上计算其对应的熵,验证齐夫定律github: ShiyuNee…...
微信小程序之个人中心授权登录
🎬 艳艳耶✌️:个人主页 🔥 个人专栏 :《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 ,越幸运。 1.了解微信授权登录 微信登录官网: 小程序登录https://developers.weixin.qq.com/miniprogram/d…...
Elasticsearch的聚集统计,可以进行各种统计分析
说明: Elasticsearch不仅是一个大数据搜索引擎,也是一个大数据分析引擎。它的聚集(aggregation)统计的REST端点可用于实现与统计分析有关的功能。Elasticsearch提供的聚集分为三大类。 度量聚集(Metric aggregation):度量聚集可以用于计算搜…...
Webpack 理解 input output 概念
一、介绍 如果还没用过 Webpack 请先阅读 Webpack & 基础入门 再回头看本文。 Webpack 的核心只做两件事,输入管理(Input Management)和输出管理(Output Management),什么花里胡哨的插件和配置都离不…...
【字符函数】
✨博客主页:小钱编程成长记 🎈博客专栏:进阶C语言 🎈相关博文:字符串函数(一)、字符串函数(二) 字符函数 字符函数1.字符分类函数1.1 iscntrl - 判断是否是控制字符1.2 i…...
git创建与合并分支
文章目录 创建与合并分支分支管理的概念实际操作 解决冲突分支管理策略Bug分支Feature分支多人协作 创建与合并分支 分支管理的概念 分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码…...
【电子通识】USB TYPE-A 2.0/3.0连接器接口
基础知识 USB TYPE-A连接器又可称为USB-A,现在不少PC、PC周边、手机充电器等等都依然采用了这种扁平的矩形接口,是目前普及度最高的USB接口了。 USB-A亦有分为插头与插座。常见的USB-A数据线的A端就是插头,而充电器上的则是插座。插头和插座…...
org.apache.sshd的SshClient客户端 连接服务器执行命令 示例
引入依赖 <dependency><groupId>org.apache.sshd</groupId><artifactId>sshd-core</artifactId><version>2.9.1</version></dependency>示例代码,可以直接执行,也可以做替换命令、维护session等修改 p…...
STM32 裸机编程 03
MCU 启动和向量表 当 STM32F429 MCU 启动时,它会从 flash 存储区最前面的位置读取一个叫作“向量表”的东西。“向量表”的概念所有 ARM MCU 都通用,它是一个包含 32 位中断处理程序地址的数组。对于所有 ARM MCU,向量表前 16 个地址由 ARM …...
Python ‘list‘ object is not callable错误
我尝试着解决“TypeError: ‘list’ object is not callable”这个错误。在Python编程中,我有时会遇到这个错误。这个错误通常是由于我错误地尝试像函数一样调用一个列表对象。为了解决这个问题,我需要找出错误发生的具体位置,然后进行修正。…...
原生php 实现redis登录五次被禁,隔天再登陆
<?php /*** Created by PhpStorm.* User: finejade* Date: 2023-10-18* Time: 11:08*/ session_start();include_once(header.php); include_once(connect.php); include_once(common.php); include_once(redis.php); try {// 常量 用户错误次数记录define("USER_LOGI…...
24. Kernel 4.19环境下,Cilium网络仍然需要使用iptables
在设计这套容器集群服务时,我从原来的k3s架构中分离出一个问题,那就是容器网络插件应该选择哪个。因为我设计的目标是给服务器领域使用的容器引擎,所以我就不需要考虑太多边缘IOT设备的情况,直接拉满技能找了cilium。cilium借助内核ebpf技术的出现,让我看到了网络性能更好…...
java中的容器(集合),HashMap底层原理,ArrayList、LinkedList、Vector区别,hashMap加载因子0.75原因
一、java中的容器 集合主要分为Collection和Map两大接口;Collection集合的子接口有List、Set;List集合的实现类有ArrayList底层是数组、LinkedList底层是双向非循环列表、Vector;Set集合的实现类有HashSet、TreeSet;Map集合的实现…...
Linux Server 终止后立即重启报错 bind error: Address already in use
先启动Server,再启动Client,然后使用CtrlC关闭Server,马上再运行Server,会得到以下结果: bind error: Address already in use这是因为,虽然Server的应用程序终止了,但TCP协议层的连接并没有完全…...
【Python 千题 —— 基础篇】分解数据
题目描述 题目描述 编写一个程序,输入一个类似 “233,234,235” 格式的字符串,然后提取字符串中的数字,将这些数字存储在列表中,并输出该列表。在这里,我们使用 eval 函数来解析字符串中的数字。 输入描述 输入一个…...
【C++】C++11新特性之右值引用与移动语义
文章目录 一、左值与左值引用二、右值与右值引用三、 左值引用与右值引用比较四、右值引用使用场景和意义1.左值引用的短板2.移动构造和移动赋值3.STL中右值引用的使用 五、万能引用与完美转发1.万能引用2.完美转发 一、左值与左值引用 在C11之前,我们把数据分为常…...
6 文件保存功能优化
6 文件保存功能优化 6.1 开发流程 流程说明 实现保存文件的功能,包含以下逻辑: 检查当前是否有已打开的文件如果没有打开的文件,弹出保存文件对话框让用户选择保存位置将文本编辑框中的内容写入到文件中 代码实现 void Widget::on_btnSave_cl…...
电信393
...
2026年6月PMP考试最后两个月:想上岸?先把这5件事搞明白!
办公室小刘在群里发了一条消息,气氛瞬间紧张起来: “刚收到基金会通知,4月16日早上10点抢考位!大家准备好没有?” 群里十几个人,只有两个人回了“准备好了”。其他人不是没完成英文报名,就是连基…...
Qwen3-32B-Chat镜像快速上手:RTX4090D优化版,开箱即用无需复杂配置
Qwen3-32B-Chat镜像快速上手:RTX4090D优化版,开箱即用无需复杂配置 1. 镜像概述与核心优势 Qwen3-32B-Chat是阿里云推出的高性能大语言模型私有部署解决方案,专为RTX 4090D显卡优化。相比通用部署方案,这个镜像有三大突出优势&a…...
【万字文档+PPT+源码】基于springboot+vue个性化课程推荐系统-计算机专业项目设计分享
【万字文档PPT源码】基于springbootvue个性化课程推荐系统-计算机专业项目设计分享 【万字文档PPT源码】基于springbootvue个性化课程推荐系统-可用于毕设-课程设计-练手学习【万字文档PPT源码】基于springbootvue个性化课程推荐系统-计算机专业项目设计分享 摘 要 随着计算机…...
Hugo Paper主题暗黑模式实现原理:深入理解CSS变量与Tailwind配置
Hugo Paper主题暗黑模式实现原理:深入理解CSS变量与Tailwind配置 【免费下载链接】hugo-paper 🪺 A simple, clean, customizable Hugo theme 项目地址: https://gitcode.com/gh_mirrors/hu/hugo-paper Hugo Paper主题是一款简洁、优雅且高度可定…...
Reek深度解析:25种代码气味检测原理详解
Reek深度解析:25种代码气味检测原理详解 【免费下载链接】reek Code smell detector for Ruby 项目地址: https://gitcode.com/gh_mirrors/re/reek Reek是一款强大的Ruby代码气味检测工具,能够帮助开发者识别代码中潜在的质量问题和设计缺陷。本文…...
【花雕学编程】Arduino BLDC 之多电机扭矩分配(差速驱动机器人)
在机器人工程领域,差速驱动(Differential Drive)因其结构简单、机动性强(可原地转向)而被广泛应用于各类移动机器人。对于采用双BLDC(无刷直流)电机作为驱动核心的差速驱动机器人,“…...
python pre-commit-hooks
## 关于Python pre-commit hooks,一些实际工作中的思考 在团队协作开发Python项目时,经常会遇到这样的场景:有人提交了代码,但忘记格式化,或者引入了语法错误,或者提交了调试用的print语句。这些问题虽然不…...
告别卡顿!用GStreamer的nvv4l2decoder插件为你的RTSP播放器开启GPU硬解
告别卡顿!用GStreamer的nvv4l2decoder插件为你的RTSP播放器开启GPU硬解 在实时视频处理领域,卡顿和延迟是开发者最头疼的问题之一。想象一下,当你正在构建一个多路视频分析系统时,CPU软解带来的高负载不仅让机器风扇狂转ÿ…...
