前言
ArrayList(这里的ArrayList是基于jdk1.7)是在项目中经常使用的集合类,例如我们从数据库中查询出一组数据。这篇文章不去剖析它的继承和实现,只是让我们知道实例化及增删改查时它的内部代码是怎么实现的。
public class TestList {
@Test
public void testArrayList(){
List<Integer> list = new ArrayList<>(); for (int i = 0; i < 12; i++) {
list.add(i);
}
}
}
实例化
List<Integer> list = new ArrayList<>();
我们先来看看上面这段实例化ArrayList时,内部发生了什么。
这调用的是ArrayList的无参构造函数,如下
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
步骤:
- 调用父类的构造函数(AbstractList的构造函数)
- 赋值elementData(一个空数组{})
这里把ArrayList内部的属性说明下,一共有DEFAULT_CAPACITY、EMPTY_ELEMENTDATA、elementData、size。其中DEFAULT_CAPACITY和EMPTY_ELEMENTDATA是被定义为static和final的。而elementData是我们添加元素时存储的数组,size就是这个数组的大小,DEFAULT_CAPACITY就是数组默认的大小,EMPTY_ELEMENTDATA是一个空数组。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10; /**
* Shared empty array instance used for empty instances.
*/
private static final Object[] 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 == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData; /**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
实例化部分就到这里了,其他有参的这里就不做介绍,需要的可以自己去看看,接下来看看添加元素
添加元素
for (int i = 0; i < 12; i++) {
list.add(i);
}
这里循环添加了12个元素,是为了查看ArrayList的第一次扩容,接下来看看ArrayList里面是怎么实现的
add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add方法中的步骤如下
- 调用ensureCapacityInternal,传入参数(size+1)
- elementData数组对象赋值
- 返回true
ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
这个方法中传入的参数名称为minCapacity,翻译为中文就是最小容量。
这个方法首先判断当前数组对象elementData是不是等于空对象EMPTY_ELEMENTDATA。大家可以看下实例化时elementData对象的赋值(如下)。
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
如果是,则最小容量minCapacity重新赋值为DEFAULT_CAPACITY和minCapacity中最大的一个数。
然后调用ensureExplicitCapacity方法。断点内容如下
minCapacity被赋值为DEFAULT_CAPACITY
接下来是ensureExplicitCapacity方法
ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount是ArrayList的父类AbstractList的一个属性,记录被修改的次数,也是通过这个触发fail-fast机制的,这里不过多说明。
下面就是比较传入的最小容量minCapacity减去elementData数组的长度是否大于0(也就是最小容量minCapacity是否大于elementData数组的长度),是的话调用grow方法。
grow方法
这个方法就是ArrayList的扩容方法,接下来看看方法内部代码
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);
}
由于是第一次调用add方法,所以minCapacity值为ArrayList的默认容量10,如下
- 接下来定义一个旧容量oldCapacity,值为elementData数组长度
- 然后定义一个新容量newCapacity,值为旧容量oldCapacity加上(旧容量oldCapacity右移1位,也就是除以2,不四舍五入)
- 接着比较新容量newCapacity和传入的最小容量minCapacity,谁大就赋值给新容量newCapacity
- 如果新容量newCapacity比定义的数组最大容量MAX_ARRAY_SIZE还大的话,就调用hugeCapacity方法,看是否抛出异常还是赋最大值
- 调用Arrays.copyOf进行数组扩容
获取元素
获取元素有三种方式
- 迭代器Iterator遍历
- 通过索引
- foreach循环获取
这里就介绍下通过迭代器Iterator遍历吧。
public Iterator<E> iterator() {
return new Itr();
}
Itr是ArrayList一个实现Iterator的内部类,通过这个对象来获取ArrayList中存储的元素。代码如下
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; public boolean hasNext() {
return cursor != size;
} @SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
总结
当我们实例化ArrayList时(调用无参构造函数),这个对象里面存储元素的数组还只是一个空数组。
在第一次调用add方法时(也就是第一次添加元素时),对存储元素的数组elementData进行第一次扩容,扩容的数组长度为ArrayList内部定义的默认容量10,。
当插入第11个数组元素时,进行第二次扩容,扩容的长度为原先的容量加上(原先容量/2),即原先容量的1.5倍。扩容的长度规则是10>>15>>22>>33...