ArrayList:
底层实现:数组
如何实现的:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
调用有参构造方法时创建数组的时候,则会创建一个长度为initialCapacity的数组,若initialCapacity为0,则还是相当于调用的无参构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
调用无参构造方法时创建数组的时候:this.elementData数组是{}故初始长度是0
而一般大多数人都会说ArrayList的初始长度是10,这是在第一次添加元素调用add(E e)方法的时候,才会初始化数组的长度为10
public boolean add(E e) {
//调用ensureCapacityInternal方法,传入新的size(因为新加一个元素之前,检查是否在容量之内)
ensureCapacityInternal(size + 1);
//给 elementData赋值之后size+1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//因为是首次调用add方法,故从add方法传过来的minCapacity=size+1,而初始size为0,所以minCapacity=1,调用calculateCapacity得到的返回结果为10
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA在ArrayList中定义为{}
//DEFAULT_CAPACITY在ArrayList中定义为10
//因为elementData还未添加值,故下面判断结果为true,则会返回一个最大值DEFAULT_CAPACITY
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//记录修改次数
modCount++;
//从ensureCapacityInternal方法中传过来的minCapacity=10,而elementData.length=0
故满足下面判断条件,则会调用grow(10)
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
//oldCapacity=0
int oldCapacity = elementData.length;
//ArrayList的扩容代码oldCapacity >> 1=0.5*oldCapacity
//所以ArrayList的扩容机制是原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//从上一行代码可知newCapacity=0,进入第一个if语句之后,newCapacity =10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//然后复制长度为newCapacity =10的数组给 elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容机制:
//ArrayList的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 得到ArrayList的旧长度
int oldCapacity = elementData.length;
//得到ArrayList的新长度=1.5*旧长度
int newCapacity = oldCapacity + (oldCapacity >> 1);
//minCapacity会在ensureExplicitCapacity传过来,如果新长度<minCapacity,则改变新长度为minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若生成的新长度比ArrayList的最大容量还要大则调用hugeCapacity方法
if (newCapacity - MAX_ARRAY_SIZE > 0)
//得到长度最大值, hugeCapacity方法最大返回Integer.MAX_VALUE
newCapacity = hugeCapacity(minCapacity);
// 拷贝数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
有参构造方法与无参构造方法的使用情况:
当假设我们有1000条数据加入ArrayList中时,如果使用无参构造,初始化数组的长度0 第一次添加元素的时候开始 长度为10 以后的每次扩容 1.5倍进行扩容10,15,22,33 ,50,75,112 。。。而数组扩容是数组拷贝的过程,非常消耗资源,此时调用有参构造方法,直接初始化长度500时,只需要扩容2次,相比较于无参时,大大节省了资源,从而提高了性能
总结:
ArrayList最大长度只能达到Integer.MAX_VALUE
ArrayList底层是数组的拷贝,所以增删较慢,而因为数组有索引机制,索引查找较快
ArrayList扩容机制是当元素超过其长度时才扩容且扩容为原来长度的1.5倍
ArrayList是线程不安全的
在数据量大的时候,我们创建ArrayList时最好自己初始化长度。