引言:ArrayList是集合中最常用的类之一,分析其源码有助于帮助我们更好的理解JDK设计者们的匠心独具。ArrayList的底层实现是包装了一个动态数组,如果你了解动态数组的数据结构,那么对于掌握ArrayList将会异常容易。
- ArrayList在集合框架中的关系
ArrayList继承自AbstractList实现了List接口,AbstractList是继承自AbstractCollection,AbstractCollection实现了List接口的方法;
- 构造器
默认构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
长度参数的构造函数
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);
}
}
集合参数的构造函数
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);//使用Arrays提供的工具类进行集合拷贝
} else {
// replace with empty array.
this.elementData = 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 = {};
默认的构造器里使用的空数组实例,为了区别上面的空数组实例
/**
* 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
真正数组中存放的元素个数
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
可分配的最大数组长度
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//这里减8是因为最大整数2^31本身需要8个byte去存储
- 往ArrayList中添加元素
默认add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;//在size+1的数组位置放入新元素E
return true;
}
我们再看ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//与默认数组长度10比较,取最大
}
ensureExplicitCapacity(minCapacity);//数组长度是否足够,否则扩容
}
ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)//判断最小需要的容量跟数组长度比较,如果不够,就扩容
grow(minCapacity);
}
扩容方法grow方法
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);//使用Arrays进行数组拷贝
}
- 往ArrayList中随机位置添加元素
add(int)方法
public void add(int index, E element) {
rangeCheckForAdd(index);//检查index是否越界
ensureCapacityInternal(size + 1); // Increments modCount!! 同add方法里面的ensureCapacityInternal,看本次插入是否需要扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//拷贝index位置之后的数组元素后移一位
elementData[index] = element;//在index位置放入元素
size++;//元素长度+1
}
- 从ArrayList中获取指定位置的对象
get(index)方法
public E get(int index) {
rangeCheck(index);//范围检查
return elementData(index);//直接返回数组索引位置元素
}
- 从ArrayList中移除元素
remove(Object)方法
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
这里最主要的就是fastRemove方法
private void fastRemove(int index) {
modCount++;
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 设置为null,方便GC回收内存
}
remove(index)方法,原理基本同fastRemove(index)方法,只是找到元素的方法不一样,这个方法通过索引直接找到元素
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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;
}
- 迭代器
iterator()方法,直接new了一个Itr内部类,实现了Iterator接口
public Iterator<E> iterator() {
return new Itr();
}
接下来我们看一下Itr类的主要方法
hasNext()方法,直至元素个数相等返回false
public boolean hasNext() {
return cursor != size;
}
next()方法
public E next() {
checkForComodification();//校验是否迭代期间有新增删除,有的话抛错ConcurrentModificationException
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];
}
- 排序方法
sort(Comparator<? super E> c),可接受传参指定的排序比较器
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
默认排序,采用ComparableTimSort算法
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);
}
TimSort 是一个归并排序做了大量优化的版本。对归并排序排在已经反向排好序的输入时表现O(n2)的特点做了特别优化。对已经正向排好序的输入减少回溯。对两种情况混合(一会升序,一会降序)的输入处理比较好
总结:
- ArrayList基于数组方式实现
- 添加元素时可能要扩容,删除元素时,将删除掉的位置元素置为null,下次gc就会回收这些元素所占的内存空间。
- 线程不安全
- add(int index, E element):添加元素到数组中指定位置的时候,需要将该位置及其后边所有的元素都整块向后复制一位
- get(int index):获取指定位置上的元素时,可以通过索引直接获取
- remove(Object o)需要遍历数组
- remove(int index)不需要遍历数组,只需判断index是否符合条件即可,效率比remove(Object o)高
- contains(E)需要遍历数组