1.ArrayList概述
1.1ArrayList特点
- ArrayList是实现List接口的动态数组
- ArrayList可以允许元素为Null
- ArrayList是线程不安全
1.2ArrayList数据结构
ArrayList底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据
2.ArrayList源码分析
2.1属性
private static final int DEFAULT_CAPACITY = 10; // 默认大小为10,
实际上调用无参构造函数初始化时,存放数据的数据是个空数组,
调用add时才会真正初始化.
private static final Object[] EMPTY_ELEMENTDATA = {};
// 使用带参构造器进行初始化,若initcapacity为0,或者传入的集合
为空时,会指向该数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 调用无参构造函数时,使用该数组
transient Object[] elementData; // 真正存放数据的数组
private int size; // 数组元素的个数
2.2构造方法
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);
}
}
带初始化容量initialCapacity的构造方法,若initialCapacity=0,则elementData 指向空数组;若elementData >0,则elementData进行初始化
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
默认构造方法。执行默认空数组
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;
}
}
带参数的构造方法,可以传入一个集合。若集合中没有元素,则指向空数组;否则,就把集合中的元素copy到elementData数组中。底层调用了本地方法System.arraycopy()。
2.3主要方法
2.3.1add():将此元素添加到列表的末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
先判断下当前elementData数组能否容纳下新增的元素,如果空间不够的话,需要先扩容,然后再插入元素。
size是数组中数据的个数,因为要添加一个元素,所以,要先判断数组能否装下(size+1)个元素
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
calculateCapacity()方法是用来计算容量的。先判断elementData是否是默认空数组(使用无参构造器构造的)
- 如果elementData是默认空数组,则这是第一次添加元素,那么size=0,minCapacity=size+1=1,通过调用Math.max()方法,会返回minCapacity=DEFAULT_CAPACITY=10
- 如果elementData是空数组,则这是第一次添加元素,那么size=0,minCapacity=size+1=1,不会调用Math.max()方法,会返回minCapacity=1
- 如果elementData不是空数组且不是默认空数组,那么size!=0,minCapacity=size+1,不会调用Math.max()方法,会返回minCapacity=size+1
// 确定是否扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 扩容
grow(minCapacity);
}
modCount:操作数(父类AbstractList中的属性)表示对这个ArrayList进行操作过了。
在ensureExplicitCapacity()方法中,首先操作数自增1,把需要的最小空间容量与数组当前实际长度进行比较:
- 如果elementData是默认空数组,它现在需要的容量是10,但elementData.length=0,所以,需要扩容
- 如果elementData是空数组,它现在需要的容量是1,但elementData.length=0,所以,需要扩容
- 如果elementData是非空数组,若它添加一个元素后需要的容量比原数组长度大,就需要扩容;否则就不需要扩容。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
grow()方法进行扩容,保证ArrayList至少能存储minCapacity个元素 。
第一次扩容,newCapacity为原先的1.5倍.
第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。如果此时capacity已经超过了ArrayList的最大容量Integer.MAX_VALUE – 8,那么newCapacity最大为Integer.MAX_VALUE.
然后调用Arrays.copyOf(elementData, newCapacity)对原先旧的数组进行复制操作,底层调用的是System.arraycopy,这是个native方法.
所以,要尽量避免扩容,数组的复制非常耗费性能!而且旧的数组需要GC!