Ask questions
- Based on what? Array? Linked list? queue?
- Why can you keep the
add
element?
analysis
Method to realize
Variables defined:
An array is maintained:
transient Object[] elementData; // non-private to simplify nested class access
private int size;
ArrayList
Everything internally operates add、remove、set、get
on elementData
this array, so the ArrayList
implementation is based on the array.
Two lengths: size
====>The length of the current list elementData.length
====> Array length
Array length ≥ List length
The default length and two default arrays:
/**
* 默认的数组长度,当我们直接创建一个ArrayList对象时,容量为10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 直接创建无参ArrayList时,内部指向该数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 创建带初始长度的ArrayList,或者传入另一个列表为参数创建对象时,如果长度为0或者传入列表长度为0,内部指向该数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
Construction method
ArrayList
Three construction methods are defined. They are non-parameter construction. One int
type parameter construction method is passed in, and a list is passed as the parameter construction method.
- Pass in the initial length. This method is generally used when we know the length of the list to avoid applying too much useless memory space
public ArrayList(int initialCapacity) {
//如果长度大于0,则创建该长度的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//长度为0,指向默认数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
- No parameter construction, will
elementData
point to the default array, length 0 - Pass in a list.
Arrays.copyOf()
Copy the passed list to the new array by method, and thenelementData
point to the address
Operation method
The internal implementation of each method is an operation on the array
get
Get the value of the corresponding subscript of the array directly
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
set
The same operation on the array, replace the value corresponding to the subscript in the array with the new value
public E set(int index, E element) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
add
The subscript is current size+1
, and the value that needs to be added is set to the value corresponding to the subscript in the array. Because the length of the array is fixed, it involves the most important expansion and growth strategy, ensureCapacityInternal(size + 1)
method
public boolean add(E e) {
//扩容算法,传入当前长度+1
ensureCapacityInternal(size + 1); // Increments modCount!!
//扩容完成后赋值
elementData[size++] = e;
return true;
}
addAll
Method, first convert the list parameter to an array, and then use the System.arraycopy method to copy the array to elementData
it, which also involves the expansion and growth strategy, but the incoming parameters are different .
public boolean addAll(Collection<? extends E> c) {
//先转为数组
Object[] a = c.toArray();
int numNew = a.length;
//扩容算法,传入当前长度+需要add的元素的数量
ensureCapacityInternal(size + numNew); // Increments modCount
//拷贝数组
System.arraycopy(a, 0, elementData, size, numNew);
//列表长度修改
size += numNew;
return numNew != 0;
}
remove
The same copy operation is performed on the array, which is similar to moving each element forward one bit after the element that needs to be removed in the array, overwriting the original value.
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
//数组拷贝
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
Expansion
Front add
and addAll
methods, there is ensureCapacityInternal1
a method for expansion algorithms
look at the relevant code expansion:
//①传入列表长度值
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//②如果计算后需要的长度大于当前数组的长度,执行扩容操作
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//③对数组进行扩容
private void grow(int minCapacity) {
// overflow-conscious code
//获取当前长度
int oldCapacity = elementData.length;
//计算新长度为旧长度 * 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新长度小于需要的长度,使用传入的长度,适用于第一次创建后添加元素的扩容和addAll方法元素很多超过原有1.5倍的情况下
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果计算后长度大于最大列表长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//通过copyOf方法将原有数据拷贝到一个新的数组对象中,再赋值给elementData,至此,扩容完成。
elementData = Arrays.copyOf(elementData, newCapacity);
}
analysis:
- First we pass in a value, which represents the length of our list after add or addAll
- You cannot expand the array every time you add , otherwise the cost will be too great, so the expansion of the array will not only increase the length by one each time.
- If it is currently an empty array, take the maximum of the default value and the passed value. In order to create an array of length 10 directly for the first time, otherwise add an element to expand once, which is expensive
- Calculate whether expansion is needed
- Expansion operation, under normal circumstances, the array capacity is expanded by 1.5 times , two special cases:
- Add element for the first time, directly expand to the default length of 10
- There are a lot of addAll elements, and the total length is more than 1.5 times the original length, directly expand to this length
- Copy all the original data to the expanded new array through the array copy method, and finally assign the array to elementData
- Expansion completed
to sum up
- ArrayList is implemented based on an array, the default length of the array is 10, all operations are operations on the maintained array
- The add and addAll methods may trigger array expansion
- When the length is not enough, the expansion length is generally 1.5 times the existing length
- Expansion, deletion, etc. are all achieved by copying the array, so the list should not be too long, otherwise each copy will consume too much performance!