ArrayList基本原理
底层是基于一个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 = {};
/**
* 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
ArrayList优缺点
缺点:
容量受限时,需要进行数据扩容,进行元素拷贝会影响性能
2.频繁删除和往中间插入元素时,产生元素挪动,也会进行元素拷贝
3.不是线程安全的。
优点:
随机访问某个元素,性能很好;
ArrayList扩容原理
1.ArrayList list = new ArrayList();
这个动作做了什么
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
可以看出,它将elementData数组的地址指向了DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址,
2.list.add(“Hello ArrayList”)这个动作探究;
/**
* 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;
}
方法ensureCapacityInternal确保内部容量的意思,
继续看ensureCapacityInternal()
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
先来看看calculateCapacity()方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
其中,minCapacity 值为1,DEFAULT_CAPACITY值为10
/**
* Default initial capacity.
* 默认初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
所以calculateCapacity方法返回值为10,即ensureExplicitCapacity(10);
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
显然minCapacity - elementData.length = 10 大于0,满足if条件,于是执行grow()方法,此方法就是ArrayList扩容原理的核心方法,
/**
* 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;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
很明显,oldCapacity的值为elementData数组的长度为0,newCapacity的值为1.5倍的oldCapacity,所以newCapacity值也为0,而minCapacity为10,所以第一个if条件成立,于是newCapacity = 10;
最后还执行elementData = Arrays.copyOf(elementData, newCapacity); 这是grow方法的另一个核心步骤,数组的拷贝;从脉络上看,这里的Array.copyOf它实参是elementData和newCapacity=10。可以看到他的内部直接调用了一个重载方法,在重载方法里,首先是一个三元表达式和一个System.arraycopy方法调用。
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
System.arraycopy(original, 0, copy,0,Math.min(original.length, newLength));这句代码应该是操作了original和copy的样子。
仔细分析,elementData的class是Object[].class的类型,可以看出三元表达式会执行(T[]) new Object[newLength]。接着就会执行System.arraycopy这个方法,你可以查阅下JDK的API,它的主要作用是数组的拷贝。原来ArrayList的缺点原因在这里。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
如果难以理解这个方法的代码,可以举个例子:
假设有两个数组src和dest,他们都有5个元素0,1,2,3,4,即src[0,1,2,3,4] dest[0,1,2,3,4]
问题:执行System.arraycopy(src, 3, dest, 1, 2)后是什么意思?
答案:就是从src源数组3位置开始,移动两个元素到dest数组1开始覆盖,结果就是dest变成[0,3,4,3,4]
所以System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));就很号理解,就是从original位置0开始移动Math.min(original.length, newLength)(此时为0)个元素到copy数组,从copy数组0位置开始覆盖,等于什么也没有拷贝,直接返回了新创建的数组T[] copy ,这个数组的长度为10。
回到最开始,内部容量确定后,直接执行elelmentData[size++] = e;通过数组的赋值操作,就完成了第一次元素的添加了。
可以发现,当添加开始的时候,如果大小不够就会进入grow方法,会进行一次1.5倍的扩容,所以代码规范一般建议我们要制定ArrayList的大小,如果不指定,可能会造成频繁的扩容和拷贝,导致性能低下
总结:
1.如果创建ArrayList不指定大小,ArrayList第一次添加元素时,会指定默认的容量大小为10。
2.ArrayList的扩容机制,通过grow方法,它的核心是一个计算扩容大小,是基于右移1位来计算,扩容大小为1.5倍,另一个核心是数组拷贝,底层通过System.arraycopy来创建新数组,来最终实现空间扩容。
最后,完善前面的流程图: