java源码分析:ArrayList的扩容机制
java源码分析:ArrayList的扩容机制
ArrayList
是java
中最常用的集合之一,底层的数据结构是数组,在学习集合时,阅读源码是必不可少的环节之一,阅读源码可以有效的帮助我们深入了解其工作原理,下面根据源码详细的介绍下扩容机制,环境为jdk1.8。
首先看看它的无参构造,ArrayList
无参构造方法如下:
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
先来看看elementData
,这是一个Object数组,也就是集合中的元素,其定义如下:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
是一个final的Object空数组,定义如下:
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
由注释我们可知这是一个共享的空数组,用于默认大小的空实例。
那么ArrayList
什么时候开始扩容呢,从add()
方法中可以找到答案,源码如下:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
// 检查是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
很明显第一句代码就是其扩容检查方法,参数是当前的集合大小+1,也就是新增元素后的数组实际大小。继续跟进ensureCapacityInternal()
方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
此处的minCapacity
就是需要的最少空间。继续跟进calculateCapacity()
方法代码:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
由此可以知道:如果集合中的元素是默认的空数组的话,那么将会返回DEFAULT_CAPACITY
与minCapacity
两者的较大值。DEFAULT_CAPACITY
定义如下:
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
所以默认的初始容量是10。再进到ensureExplicitCapacity()
方法中来看:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 如果需要的最少空间大于目前的数组长度,则需要扩容
if (minCapacity - elementData.length > 0)
// 扩容方法
grow(minCapacity);
}
这里的modCount
是指数组结构被修改的次数,从代码中可以看到如果需要的最小空间大于当前集合的长度的话就会调用grow()
方法,方法定义如下:
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 右移等于除2,也就是说新容量等于旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 这里会判断扩容后的容量是否会溢出
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
此处的grow()
方法就是最终的扩容方法,if (newCapacity - MAX_ARRAY_SIZE > 0)
这个判断就很有意思了,可能有人会想为什么不写成if (newCapacity > MAX_ARRAY_SIZE)
呢?他们表达的是同一个意思吗?答案:不是。因为如果扩容后newCapacity
溢出的话,那么它就会变成负数,前者的判断就会返回true
,后者则会返回false
,两者相反。
再继续看如果溢出会怎么样:
private static int hugeCapacity(int minCapacity) {
// 小于0说明是负数,所以判断为溢出,此时抛出内存溢出错误
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
由源代码可知:如果需要的最小空间大于最大的数组空间的话(前面说过最大数组空间)那么新的数组大小就是Integer
的最大值,否则为最大数组空间。
总结:
当每次添加新元素时,ArrayList
都会检查是否需要扩容,如需扩容,会扩容成当前容量的1.5倍,如果溢出(负数)的话则会抛出OutOfMemoryError
,如果介于Integer.MAX_VALUE
和MAX_ARRAY_SIZE
两者之间,则取Integer.MAX_VALUE
。
下一篇: 艺术品构件堆叠问题
推荐阅读
-
Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
-
Java中的容器(集合)之ArrayList源码解析
-
[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
-
【Java】NIO中Selector的select方法源码分析
-
ArrayList的扩容机制
-
别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
-
死磕 java集合之ArrayList源码分析
-
【SpringBoot】默认错误处理机制的源码大白话分析,以及自定义配置(遇坑)
-
java ArrayList 迭代器快速失败源码分析
-
Java ArrayList源码分析(有助于理解数据结构)