Exploring the source code of ArrayList: the implementation behind Java dynamic array
1. Member variables
Readers need to read the member variables of the source code first, and look at them familiarly, which will help the understanding of the source code later
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空数组(用于空实例)。
*/
private static final Object[] EMPTY_ELEMENTDATA = {
};
//用于默认大小空实例的共享空数组实例。
//我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
/**
* 存储ArrayList元素的数组缓冲区
*/
transient Object[] elementData;
/**
* ArrayList 所包含的元素个数
*/
private int size;
Two, the constructor
1. Default constructor
/**
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
When an ArrayList is created with a parameterless construction method, an empty array is actually initialized and assigned. At this time, no object is created for it, and the capacity is actually allocated when the operation of adding elements to the array is actually performed. That is, when the first element is added to the array, the capacity of the array is expanded to 10.
2. Constructor with initial capacity parameter
/**
* 带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//初始容量大于0
//创建initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//初始容量等于0
//创建空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3. Specify the collection element parameter constructor
/**
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
*如果指定的集合为null,throws NullPointerException。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
3. Add() method expansion mechanism
Before entering the core source code expansion mechanism of ArrayList, we first need to have a preliminary understanding of some variables involved in the source code, which will help you gain an in-depth understanding of the source code
minCapacity
: the minimum capacity required by the arrayelementData
: Array buffer that stores ArrayList elements
public boolean add(E e) {
ensureCapacityInternal(size + 1); // size = 0
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//minCapacity = 1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//elementData = {}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判断elementData是否为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10;minCapacity = 1
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
//minCapacity = 1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//ensureExplicitCapacity(10)
}
private void ensureExplicitCapacity(int minCapacity) {
//minCapacity = 10
modCount++;
if (minCapacity - elementData.length > 0)//elementData.length = 0
grow(minCapacity);
}
private void grow(int minCapacity) {
//minCapacity = 10
int oldCapacity = elementData.length;//oldCapacity = 0
int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity = 1.5*oldCapacity = 0
if (newCapacity - minCapacity < 0)//minCapacity = 10
newCapacity = minCapacity;//newCapacity = 10
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//利用Arrays.copyOf()方法进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- Add a new element, the minimum capacity required at this time is
minCapacity = size+1
- First determine
elementData
whether the underlying array is an empty array- If it is an empty array, update it
minCapacity
to the default capacity of 10, - If not an empty array, make
minCapacity
no changes to
- If it is an empty array, update it
- Determine whether the required minimum capacity
minCapacity
is greater than the length of the buffer arrayelementData
:- If it is larger, expand the capacity
grow()
, - otherwise do not process
- If it is larger, expand the capacity
- During capacity expansion
grow()
, firstelementData
expand the length to 1.5 times the original length, and then judge the expanded lengthelementData
and the required minimum capacityminCapacity
- If the length of after expansion
elementData
is still smaller thanminCapacity
the length of , it is still not enough. At this time,minCapacity
the length of is directly assigned toelementData
- Otherwise, go directly to the next step
- If the length of after expansion
- Finally, it is necessary to judge
elementData
whether the length of is beyond the maximum valueMAX_ARRAY_SIZE
- If the maximum value is exceeded, see if the required minimum capacity
minCapacity
is greater than the maximum valueInteger.MAX_VALUE
- If not, expand the length of the array to the maximum value of the array
MAX_ARRAY_SIZE
, if yes, returnInteger.MAX_VALUE
- If the maximum value is exceeded, see if the required minimum capacity
4. Scene analysis
1. For the ensureExplicitCapacity() method
1.1 When adding the first element to ArrayList
When we want to add
enter the first element to ArrayList
, elementData.length
it is 0 (because it is still an empty list), because ensureCapacityInternal()
the method is executed, so minCapacity
it is 10 at this time. At this time, minCapacity - elementData.length > 0
it is established, so it will enter grow(minCapacity)
the method.
1.2 When adding the second element
When the second element is added, minCapacity
it is 2. At this time, elementData.length(容量)
the capacity is expanded to 10 after adding the first element. At this time, minCapacity - elementData.length > 0
it is not true, so the method will not be entered (executed) grow(minCapacity)
.
When adding the 3rd, 4th... to the 10th element, grow
the method will still not be executed, and the capacity of the array is 10.
1.3 until the 11th element is added
minCapacity(为 11)
is greater than elementData.length
(is 10). Enter grow
the method to expand.
2. For the grow() method:
2.1 When adding the first element
oldCapacity
is 0, the first if judgment is established after comparison, newCapacity = minCapacity(为 10)
. But the second if judgment will not be true, that is, newCapacity
is not MAX_ARRAY_SIZE
greater than , it will not enter hugeCapacity
the method. The capacity of the array is 10, return true in the add method, and the size is increased to 1.
2.2 When the 11th element of add enters the grow method
newCapacity
is 15, the ratio minCapacity(为 11)
is larger, and the first if judgment is not established. If the new capacity is not greater than the maximum size of the array, it will not enter hugeCapacity
the method. The capacity of the array is expanded to 15, return true in the add method, and the size is increased to 11. And so on...
5. Experience
- Variables are understood as the minimum capacity
minCapacity
required by the underlying array when we add elements into the ArrayList collection - Variables
elementData.length
are understood as we add elements into the ArrayList collection, the maximum capacity of the underlying array - When the minimum capacity
minCapacity
> maximum capacityelementData.length
, the capacity expansion mechanism will be triggered. - We always add elements to the ArrayList collection frequently, and developers have thought of this, so in the expansion mechanism of the ArrayList collection, when we add the first element, we directly set it to 10. Here, it can be understood that because we need to add elements frequently in the future, in order not to always trigger the expansion mechanism of the collection, we "lie" that the minimum capacity required is 10, so the system directly sets it
minCapacity
toelementData.length
10