Java的ArrayList扩容源码解析
ArrayList扩容源码
1、扩容原理概括
ArrayList的扩容原理如下:
- 初始容量:在创建ArrayList对象时,默认会分配一个初始容量。初始容量可以通过构造函数进行指定,若未指定,则默认为10。例如,ArrayList<String> list = new ArrayList<>();会创建一个初始容量为10的ArrayList。
- 添加元素:当向ArrayList中添加元素时,会先检查当前元素个数是否已经达到了数组的容量上限。如果元素个数等于或超过了容量上限,就需要进行扩容。
- 扩容机制:扩容时,ArrayList会创建一个更大的新数组,并将原有的元素复制到新数组中。默认情况下,新数组的大小是原来容量的1.5倍(即增长50%)。例如,如果当前容量为10,那么扩容后的新容量为15。
- 数组复制:在扩容时,ArrayList使用System.arraycopy()方法将原始数组中的元素复制到新的数组中。这是一个底层的高效数组复制方法,可以快速地将原有的数据移动到新数组中。
- 更新引用:在完成数组复制后,ArrayList会更新内部的引用,指向新的数组。这样,原先的数组就会被垃圾回收器回收。
通过动态扩容,ArrayList能够在添加元素时保持高效的性能。扩容操作是有一定开销的,但由于扩容的时间复杂度为O(n),其中n是当前元素个数,所以平均情况下,每次添加元素的时间复杂度仍然是O(1)。
同时,扩容的增长因子(即容量的增加比例)也可以被修改,以满足特定需求。
2、源码解析
例如,对于如下list进行添加元素,初始容量设置为5,添加6个元素:
ArrayList<String> list = new ArrayList<>(5); list.add("1"); list.add("2"); list.add("3"); list.add("777"); list.add("888"); list.add("999");
对于一开始的前五个元素而言:
public boolean add(E e) { modCount++; add(e, elementData, size);//e是“1”,size是0,即目前元素数量,也可以理解为数组的索引 return true; } //add源码: private void add(E e, Object[] elementData, int s) { if (s == elementData.length)//当元素数量等于arraylist数组长度,才grow扩容 elementData = grow(); elementData[s] = e;//否则直接在索引位置赋值 size = s + 1;//并将索引右移 }
但在第6个元素“999”添加进去的时候,要进入grow方法进行扩容:
private void add(E e, Object[] elementData, int s) { //e是“999”,s为5 if (s == elementData.length)//已经相等 elementData = grow();//步入grow elementData[s] = e; size = s + 1; } //grow源码:输入size + 1 private Object[] grow(int minCapacity) { int oldCapacity = elementData.length;//旧的数组大小 //接下来,检查当前数组是否为空,或者不是使用默认初始容量的空数组(即DEFAULTCAPACITY_EMPTY_ELEMENTDATA)。如果是空数组,则说明是第一次添加元素,使用默认初始容量。 if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */);//计算新数组大小 return elementData = Arrays.copyOf(elementData, newCapacity); } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } } //ArraysSupport.newLength源码: public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 //minGrowth就是6 - 5 = 1,即,至少需要增大的容量 //prefGrowth为数组的旧容量的1/2 //求其max,一般为1/2。加上oldlength,所以为1.5倍 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); } //最后调用Arrays.copyOf,内部使用System.arraycopy()方法将原始数组中的元素复制到新的数组中
实现扩容后,添加元素步骤同上,结束,return true
到此这篇关于Java的ArrayList扩容源码解析的文章就介绍到这了,更多相关ArrayList扩容源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Spring Cloud Alibaba配置多环境管理详解与实战代码
本文通过实际案例详细介绍了springboot配置多环境管理的使用,以及基于nacos的配置多环境管理的实践,在实际开发中,配置多环境管理是一个很难避开的问题,同时也是微服务治理中一个很重要的内容,感兴趣的朋友跟随小编一起看看吧2024-06-06Springboot 项目读取Resources目录下的文件(推荐)
这篇文章主要介绍了Springboot 项目读取Resources目录下的文件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-11-11Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
这篇文章主要介绍了Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture,本文直接给出实现代码,代码中包含详细注释,需要的朋友可以参考下2015-06-06
最新评论