//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//空数组,有参构造函数,参数为0时,将elementData数组赋值为EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {};
//空数组,无参构造函数时,将elementData数组赋值为该空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
初始化
通过构造函数进行初始化
1.无参构造函数
//无参构造函数
public ArrayList() {
//将底层数组Object[] elementData赋值为默认空数组,此时数组容量为0,只有add数据时,才会分配默认初始容量10.
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
将elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认空数组),虽然ArrayList()的默认初始容量为10,但这里数组为空,此时调用size(),结果为0。
常出问题的点:
List<List<Integer>> list = new ArrayList<>();
list.get(1).add(0);
//错!因为此时为空数组,就越界了(可能没有人像我这么蠢吧……)
2.有参构造函数
//有参构造函数
public ArrayList(int initialCapacity) {
//判断参数大小
if (initialCapacity > 0) {
//
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//初始容量等于0,赋值为一个空数组。
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
判断参数大小,对elementData数组进行初始化。
这里!虽然对数组大小进行了初始化,但是list没有,此时size()还是为0!
看,依然越界!

3.有参构造函数,参数为collection接口
先将传入参数toArray(),如果得到的数组长度不为0,那么判断数组类型是否是Object[]类型,因为ArrayList的底层是Object[]数组,如果不是,则使用Arrays.copyOf(…)的方法进行拷贝,内部会创建一个Object[]类型的数组,长度为size,进行拷贝,使得得到的elementData是Object[]数组。
//List<Integer> path = new ArrayList<>();
//List<List<Integer>> list = new ArrayList<>(path);
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;
}
}
这里toArray()调用的时Collection接口的toArray()方法,具体调用哪个类的toArray()方法应该是看c的实现类。
ArrayList里的toArray()方法:
将elementData数组中的元素拷贝到长度为size的Object[]数组中,并返回该数组。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
接收T类型的数组,返回一个T类型的数组
主要对比a.length和list中的元素个数size()
如果a.length<size,使用Arrays.copyOf方法进行拷贝,创建一个长度为size的新数组接收elementData中的元素, 之前传入的数组a已经没用了。
如果a.length >= size,直接调用System.arraycopy方法,将elementData中的元素拷贝到a中。
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
新增
add(E e)
public boolean add(E e) {
//先判断容量是否足够,不够需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//添加数据
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
这里! 如果是第一次add数组,那么elementData就为默认空数组,此时返回默认容量和最小容量的最大值(其实应该就是返回的默认容量吧?毕竟size==0,size+1 == 1 < 10? 但我感觉我理解的有错)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//容量不够,跳去扩容
grow(minCapacity);
}
add(int index,E element)
public void add(int index, E element) {
//判断索引是否在范围内,如果index>size||index<0,抛异常。
rangeCheckForAdd(index);
//判断容量是否足够
ensureCapacityInternal(size + 1); // Increments modCount!!
//将elementData中[index,elementData.length-1]这个区间上的数拷贝到elementData的[index+1,index+1+size-index-1]区间上。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素赋值给index位置上。
elementData[index] = element;
size++;
}
扩容
新容量 = 原容量+原容量/2;
如果新容量<需要容量,那么新容量=需要容量
如果新容量>数组最大大小,那么新容量=hugeCapacity(minValue)
然后将原数组元素复制到新数组。
(下次一定用个好看的工具画图)

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//1.8之后,采用位运算,右移一位,效率更高了。
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
删除
public E remove(int index)
其实add(int index, E element)的原理是一样的,就是数组复制。
public E remove(int index) {
//判断索引是否越界
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
//将[index+1,size-1]的数据复制到[index,size-2],然后将size-1位置上的值置空。
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;
}
参考资料
1.源码
2.https://blog.youkuaiyun.com/qq_35190492/article/details/103883964
3.https://blog.youkuaiyun.com/littlehaes/article/details/105552512
深入剖析ArrayList的构造函数、初始化过程、扩容机制、增删操作及关键属性,揭示其内部实现细节。
1106

被折叠的 条评论
为什么被折叠?



