96SEO 2026-04-23 03:25 0
在Java开发的江湖里ArrayList就像是那个Zui熟悉的陌生人。你每天dou在用它,甚至闭着眼睛douNeng敲出new ArrayList<>,但真要被问到几个刁钻的底层原理,是不是心里也会突然“咯噔”一下?

说实话,hen多人对它的理解还停留在“动态数组”这四个字上。Ru果你去面试,面试官冷不丁问一句:“ArrayList的默认初始容量到底是多少?”我敢打赌,至少有一半人会脱口而出“10”。嘿,这就掉坑里了。今天咱们就不整那些虚头巴脑的概念,直接扒开源码的底裤,kankan这玩意儿到底是怎么运作的。
咱们先来聊聊那个经典的误区。为什么大家dou觉得初始容量是10?因为书上dou这么写,DEFAULT_CAPACITY确实是10。但是请注意,这是“默认”容量,不是“初始”容量。
当你自信满满地写下这行代码时:
List list = new ArrayList<>;
在JDK 1.8里底层到底发生了什么?是不是直接给你分配了一个长度为10的数组?
完全不是。源码里写得清清楚楚,它是这么干的:
private static final Object DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
kan到了吗?它只是把一个名为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空数组常量赋值给了elementData。这时候,数组的长度其实是0!没错,是0,不是10。这其实是一种延迟初始化的策略,目的是为了节省内存。毕竟你创建了列表但不一定立马往里面塞东西,Ru果一上来就占用10个Object的空间,那得浪费多少内存啊?
那什么时候变成10了呢?当你第一次调用add方法的时候。这时候,它会去计算容量,发现当前是空数组,于是“啪”的一下把容量扩到了10。所以准确的说法是:无参构造的ArrayList,初始容量为0,第一次扩容后的容量为10。
当然Ru果你一开始就hen有远见,知道要存多少数据,直接用new ArrayList<>,那它就会老老实实给你创建一个长度为20的数组。这里还有个小细节,Ru果你传的是0,它会用另一个空数组常量EMPTY_ELEMENTDATA,以此来区分“我是默认构造的空”和“我是指定容量0的空”。这设计,是不是有点小心机?
既然初始容量可Neng是0,那它怎么变大的呢?这就是ArrayList的核心——动态扩容。
当你往列表里塞元素,塞到满了怎么办?这时候,grow方法就要登场了。这个方法决定了ArrayList的性Neng表现。
hen多人以为扩容就是简单的“容量翻倍”,比如10变20,20变40。但在ArrayList里它是这么算的:
int newCapacity = oldCapacity + ;
这里用到了位运算,oldCapacity>> 1相当于除以2。所以新容量 = 旧容量 + 旧容量的一半,也就是1.5倍。
为什么是1.5倍而不是2倍?这其实是一个权衡的结果。Ru果扩容倍数太大,虽然扩容次数少了但每次dou要浪费hen多内存空间;Ru果倍数太小,扩容就会太频繁,导致性Neng低下。1.5倍,据说是一个经过数学验证的“黄金分割”,既避免了频繁扩容,又不会浪费太多空间。
扩容的过程其实hen“暴力”。它需要Zuo三件事:
算新大小按1.5倍算,Ru果算出来的还不够用,那就以你需要的大小为准。
搬家调用Arrays.copyOf,把老数组里的数据全部拷贝到新数组里。
换房把elementData的引用指向新数组。
这里Zui耗时的就是“搬家”。Arrays.copyOf底层调用的其实是System.arraycopy,这是一个Native方法,直接操作内存。虽然比Java循环快,但数据量大了还是hen伤。所以Ru果你Neng预估数据量,比如知道要存10000个元素,Zui好在new的时候就指定容量,别让它在中间折腾个七八次扩容,那性Neng可是天壤之别。
说完添加,咱们再说说删除。你以为remove就是把那个位置设为null吗?太天真了。
数组是连续的内存空间,你把中间拔掉一根萝卜,那个坑得填上啊!所以ArrayList的删除操作,其实是一场“大迁徙”。
当你删除第i个元素时它会把第i+1个到Zui后一个元素,全部向前移动一位。这又要调用System.arraycopy了。所以删除操作的时间复杂度是O,越靠前的元素删除,移动的数据越多,越慢。
这里有个特别值得注意的细节,也是体现源码作者功力的地方:
elementData = null; // clear to let GC do its work
在把元素搬完之后它会把数组原本的Zui后一个位置显式地赋值为null。为什么要多此一举?
为了GC!数组本身持有对象的引用,Ru果你不把那个位置置空,哪怕列表的size变小了那个对象依然被数组引用着,GC就以为它还活着,没法回收。时间一长,内存泄漏就来了。这一个小小的null赋值,就是防止内存泄漏的Zui后一道防线。
Ru果你要删除一堆元素,比如把列表里所有偶数dou删了你会怎么Zuo?写个循环,一个一个remove?那你可就惨了。刚才说了删除一个就要移动一次数据,删N个就是N次移动,性Neng直接爆炸。
这时候就该用removeAll或者removeIf了。咱们kankanremoveAll的底层实现batchRemove,那叫一个精妙。
它没有傻傻地遇到一个就删一个,而是用了双指针的思路:
它定义两个指针w和r,dou从0开始。
r指针负责遍历数组,检查每个元素是否需要保留。
Ru果需要保留,就把这个元素赋值给w指针的位置,然后w往后挪一位。
Ru果不需要保留,r继续往后走,w不动。
等遍历完了w指针之前的就是所有要保留的元素,而且它们Yi经按顺序紧凑地排列在数组头部了。Zui后把w后面的元素全部置为null,geng新size。
这整个过程,只需要遍历一次数组,没有任何重复的数据移动。这比循环调用remove效率高得不是一星半点。所以下次Zuo批量操作的时候,千万别再傻傻地写for循环了用removeIf吧,它香得hen。
Zui后咱们得聊聊那个著名的异常:ConcurrentModificationException。
你肯定遇到过这种情况:用foreach循环遍历列表的时候,顺手调了个list.remove,结果程序瞬间崩了抛出这个异常。这就是传说中的Fail-Fast机制。
这是怎么回事呢?ArrayList里维护了一个叫modCount的变量,每次你调用addremove这些修改结构的方法,它dou会自增:modCount++。
当你创建迭代器的时候,迭代器会偷偷记下当前的modCount,叫expectedModCount。每次迭代器取下一个元素之前,dou会拿expectedModCount和Zui新的modCount比一下。
Ru果你在遍历的时候,直接调了list.remove,列表的modCount变了但迭代器的expectedModCount没变。这一比,发现不对劲:“哎?怎么有人动了我的奶酪?”于是直接抛异常,赶紧结束,免得数据乱套。
那怎么在遍历的时候删元素呢?
别用list.remove,用iterator.remove。因为迭代器的remove方法里在删除元素后会把expectedModCountgeng新成Zui新的modCount,这样就Neng瞒天过海,顺利删除了。当然Java 8之后geng推荐用list.removeIf,代码geng简洁,也不用操心这些破事。
回过头来kan,ArrayList虽然简单,但里面的门道真不少。从延迟初始化的空数组,到1.5倍的扩容策略,再到防止内存泄漏的null赋值,以及高效的双指针批量删除,每一个细节dou体现了JDK开发者对性Neng和内存的极致追求。
hen多时候,我们觉得技术枯燥,是因为只停留在“怎么用”的层面。一旦你钻进源码,去思考“为什么这么设计”,你会发现里面充满了逻辑的美感和解决问题的智慧。下次面试的时候,Ru果再有人问你ArrayList,别只回答“它是动态数组”了把这些细节抛出来面试官绝对会对你刮目相kan。
技术这东西,懂了就是懂了装是装不出来的。希望这篇文章Neng帮你彻底搞懂ArrayList,别再Zuo那90%答错的人了。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback