17-手写ArrayList:从0实现动态数组
手写ArrayList从0实现动态数组彻底搞懂自动扩容开篇你真的理解ArrayList吗日常开发中ArrayList是最常用的集合。但面试官追问ArrayList底层是怎么扩容的为什么默认容量是10删元素时为什么要System.arraycopy很多人就答不上来。最好的学习方式就是手写一遍。本文从0实现一个简易ArrayList把扩容、增删改查、迭代器原理全部讲透。一、ArrayList的本质ArrayList底层就是一个Object数组加上一个size计数器记录有效元素个数。Object[] elementData int size数组一旦创建长度就固定ArrayList的动态只是个假象容量不够时新建更大数组把旧数据拷贝过去。二、手写ArrayList骨架2.1 基本结构publicclassMyArrayListE{privateObject[]elementData;// 存元素的数组privateintsize;// 有效元素个数publicMyArrayList(){this(10);// 默认容量10}publicMyArrayList(intinitialCapacity){elementDatanewObject[initialCapacity];}publicintsize(){returnsize;}}【面试高频】JDK1.7中ArrayList初始化时就创建长度10的数组JDK1.8优化为延迟初始化首次add时才创建。三、add方法与扩容原理3.1 add实现publicbooleanadd(Ee){// 1. 检查是否需要扩容ensureCapacity(size1);// 2. 存入元素size1elementData[size]e;returntrue;}privatevoidensureCapacity(intminCapacity){if(minCapacityelementData.length){grow(minCapacity);}}3.2 扩容核心逻辑privatevoidgrow(intminCapacity){intoldCapacityelementData.length;// 新容量 旧容量 * 1.5intnewCapacityoldCapacity(oldCapacity1);// 处理新容量不够的边界情况if(newCapacityminCapacity){newCapacityminCapacity;}// 创建新数组拷贝旧数据elementDataArrays.copyOf(elementData,newCapacity);}【面试高频】ArrayList扩容是1.5倍计算方式是oldCapacity (oldCapacity 1)。用位运算比除法更高效。3.3 为什么是1.5倍太小如1.2倍频繁扩容频繁创建数组性能差太大如2倍浪费内存空间1.5倍是空间和时间的折中选择【面试陷阱】Vector扩容是2倍因为Vector是线程安全的扩容开销相对锁来说占比小。四、get与set方法4.1 get实现publicEget(intindex){rangeCheck(index);// 越界检查return(E)elementData[index];}privatevoidrangeCheck(intindex){if(indexsize||index0){thrownewIndexOutOfBoundsException(Index: index, Size: size);}}4.2 set实现publicEset(intindex,Eelement){rangeCheck(index);EoldValue(E)elementData[index];elementData[index]element;returnoldValue;}set返回旧值这是个容易忽略的细节。五、remove方法与数组拷贝5.1 按索引删除publicEremove(intindex){rangeCheck(index);EoldValue(E)elementData[index];// 计算需要移动的元素个数intnumMovedsize-index-1;if(numMoved0){System.arraycopy(elementData,index1,elementData,index,numMoved);}// 最后一位置null帮助GCelementData[--size]null;returnoldValue;}5.2 为什么要System.arraycopy删除中间元素后后面所有元素要整体前移一位。手动写循环效率低System.arraycopy是native方法直接操作内存性能最高。删除索引2的元素 [A, B, C, D, E, null] size5 ↓ [A, B, D, E, null, null] size4【面试高频】ArrayList的删除是O(n)操作因为要移动元素。这也是LinkedList存在的价值。5.3 按元素删除publicbooleanremove(Objecto){if(onull){for(inti0;isize;i){if(elementData[i]null){fastRemove(i);returntrue;}}}else{for(inti0;isize;i){if(o.equals(elementData[i])){fastRemove(i);returntrue;}}}returnfalse;}【面试陷阱】按元素删除用equals比较不是。所以自定义类必须重写equals。六、迭代器原理6.1 为什么不用for循环遍历删除for(inti0;ilist.size();i){if(list.get(i).equals(a)){list.remove(i);// 会导致索引错乱}}删除后size变化后面元素前移导致跳过下一个元素。6.2 手写迭代器publicclassMyIteratorE{privateObject[]elementData;privateintsize;privateintcursor;// 下一个要返回的索引publicMyIterator(Object[]elementData,intsize){this.elementDataelementData;this.sizesize;}publicbooleanhasNext(){returncursorsize;}SuppressWarnings(unchecked)publicEnext(){return(E)elementData[cursor];}}6.3 fail-fast机制JDK的ArrayList迭代器有modCount检查遍历过程中如果用list.remove修改结构会抛ConcurrentModificationException。正确做法是用迭代器的remove方法它会同步更新modCount。七、完整测试publicclassTest{publicstaticvoidmain(String[]args){MyArrayListStringlistnewMyArrayList();// 测试add和扩容for(inti0;i15;i){list.add(元素i);}System.out.println(size: list.size());// 15// 测试getSystem.out.println(list.get(0));// 元素0System.out.println(list.get(14));// 元素14// 测试setlist.set(0,新元素);System.out.println(list.get(0));// 新元素// 测试removelist.remove(0);System.out.println(list.get(0));// 元素1System.out.println(size: list.size());// 14}}八、与JDK源码的对比维度我的实现JDK实现默认容量1010延迟初始化扩容倍数1.51.5删除方式arraycopyarraycopy序列化无重写writeObject/readObjectfail-fast无modCount机制并发修改无保护抛CME异常【面试高频】ArrayList用transient修饰elementData自定义序列化只写有效元素节省空间。九、性能对比操作ArrayListLinkedList随机访问O(1)O(n)头部插入O(n)O(1)尾部插入平均O(1)O(1)中间插入O(n)O(n)删除O(n)O(n)内存占用紧凑每个节点额外存前后指针【面试陷阱】不要以为LinkedList插入删除一定比ArrayList快。中间位置插入LinkedList也要先遍历到位置时间复杂度也是O(n)。十、开发踩坑实录坑 1边遍历边删除for(Strings:list){if(s.equals(a)){list.remove(s);// ConcurrentModificationException}}正确做法用迭代器remove或Java 8的removeIf。坑 2subList修改影响原ListListIntegersublist.subList(1,3);sub.set(0,100);// 原list也被修改原因subList返回的是视图不是副本。坑 3Arrays.asList不能addListIntegerlistArrays.asList(1,2,3);list.add(4);// UnsupportedOperationException原因返回的是Arrays内部类不是真正的ArrayList。十一、面试速记卡11.1 核心知识点知识点答案底层结构Object数组默认容量10JDK8延迟初始化扩容倍数1.5倍扩容方式Arrays.copyOf删除元素System.arraycopy前移是否线程安全否随机访问O(1)序列化transient修饰数组自定义序列化11.2 高频面试题ArrayList底层是什么默认容量是多少ArrayList扩容机制是怎样的为什么是1.5倍ArrayList和Vector有什么区别ArrayList和LinkedList有什么区别ArrayList的remove是怎么实现的为什么遍历时删除会抛ConcurrentModificationExceptionArrayList用transient修饰数组的原因ArrayList在多线程下会有什么问题11.3 口诀底层Object数组默认容量是10 扩容一点五倍Arrays.copyOf来拷贝 删除arraycopy前移最后一位置null 随机访问O一插入删除O n transient修饰数组自定义序列化省空间十二、小结手写一遍ArrayList你对扩容、删除、迭代器原理都会有深刻理解。面试时被问到ArrayList能从源码角度回答比背八股强一百倍。记住ArrayList的核心数组1.5倍扩容System.arraycopy。这三个点搞懂ArrayList的面试题基本都能应对。

相关新闻

2026年实测:3大维度拆解宁波小学数学小升初机构

2026年实测:3大维度拆解宁波小学数学小升初机构

每年升学季,宁波家长的焦虑清单上总少不了两件事:一是孩子能不能够到镇海中学、效实中学这类本地头部高中的分数线,二是在小学高段这个关键的蓄力期,到底该不该给孩子报课外辅导,报什么样的辅导才能真正帮孩子实现学习…

2026/10/19 16:38:22 阅读更多 →
电路面试问题汇总一

电路面试问题汇总一

1、基尔霍夫定理的内容 2.单片机上电后没有运转,首先要检查什么 3.控制单端阻抗为50欧姆、75欧姆的信号有哪些、差分阻抗为90欧姆、100欧姆、120欧姆的信号有哪些 4.EDA 软件(如 PROTEL)进行设计(包括原理图和 PCB 图)到调试出样机的整个过程1.基尔霍夫定理的内容 基…

2026/10/19 16:30:40 阅读更多 →
Ontology Agent怎么用图谱查询做业务推理——一个客户流失预警的实战拆解

Ontology Agent怎么用图谱查询做业务推理——一个客户流失预警的实战拆解

普通的Agent能回答"这个客户最近投诉过几次",但回答不了"这个客户为什么最近投诉变多,背后是不是有产品质量问题,是不是有流失风险"。后者需要业务推理——把投诉记录、生产批次、产品缺陷率、客户等级、合同状态这些分散…

2026/10/17 10:22:28 阅读更多 →

最新新闻

Kafka消息顺序性真相:从分区内有序到生产消费端全链路保序实践

Kafka消息顺序性真相:从分区内有序到生产消费端全链路保序实践

搞过分布式消息的同学应该都遇到过这类问题:明明往Kafka里发消息的时候还想着要顺序,等消费端一跑起来,订单状态直接被回滚回退,或者用户余额扣减顺序反了,一时半会又查不出问题在哪。我之前接手过一个订单系统&#x…

2026/10/20 13:39:46 阅读更多 →
AI数字员工源码系统:从源码拆解到项目快速交付的实战分享

AI数字员工源码系统:从源码拆解到项目快速交付的实战分享

从源码到落地:一个可定制AI数字员工系统让我彻底理解“接单必备”这四个字常年在技术圈接定制化项目的老手,应该都有同感:客户真正想要的不是一个能聊天的机器人,而是一个能帮他干活、能按他业务逻辑办事的“数字员工”。我自己也…

2026/10/20 13:39:46 阅读更多 →
Simulink与X-Plane 10 UDP通信实战:飞控半实物仿真环境搭建

Simulink与X-Plane 10 UDP通信实战:飞控半实物仿真环境搭建

简介:这份资源面向航空航天控制、飞控算法验证及飞行仿真方向的工程师与研究人员,提供Simulink与X-Plane 10之间基于UDP协议的双向通信方案。包内包含发送控制指令与接收飞行数据两类核心模块,以及直线起飞控制示例模型,可帮助读者…

2026/10/20 13:39:46 阅读更多 →
ChatGPT Intelligent UI逆向解析:状态驱动的AI前端架构

ChatGPT Intelligent UI逆向解析:状态驱动的AI前端架构

1. 这不是“破解”,而是对现代AI交互范式的系统性解剖最近在几个技术社区里,陆续看到有人贴出对 ChatGPT Intelligent UI 的逆向分析报告——注意,这里说的不是模型本身,也不是API调用逻辑,而是你每天打开网页、点击输…

2026/10/20 13:39:46 阅读更多 →
警惕AI领域虚假信息:辨析OpenAI相关误传与事实核查方法

警惕AI领域虚假信息:辨析OpenAI相关误传与事实核查方法

我无法根据当前输入生成符合要求的博文。原因如下:输入中仅提供了项目标题"OpenAI 发布 Day 6 重置",但未提供任何实质性的【项目正文】、【关键词】或【摘要描述】。标题本身存在严重事实性问题:OpenAI 官方从未发布过名为“Day 6…

2026/10/20 13:39:46 阅读更多 →
智能驾驶感知算法岗面试高频考点与实战指南

智能驾驶感知算法岗面试高频考点与实战指南

智能驾驶感知算法岗的面试,跟我平时帮人做模拟面试时看到的准备方式,几乎不在一个频道上。很多人拿到一份八股题集就开始背:Transformer结构、Focal Loss公式、NMS流程、BEV感知演进……背得滚瓜烂熟,结果一进面试间,被…

2026/10/20 13:38:46 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →