ArrayList是Java集合框架中常用的数据结构之一,它可以动态地增加或删除元素,并且支持随机访问和快速插入、删除操作。本文将深入分析ArrayList的底层源码实现,帮助读者更好地理解ArrayList的工作原理。
ArrayList类的概述
ArrayList是Java集合框架中的一个类,它继承了AbstractList类,实现了List接口,可以用来存储任意类型的对象。它的底层实现基于数组,支持随机访问和快速插入、删除操作。
在ArrayList类中,有两个重要的成员变量:elementData和size。elementData是一个Object类型的数组,用来存储ArrayList中的元素;size是一个int类型的变量,表示ArrayList中元素的个数。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8683452581122892189L;
// 默认容量为10的空Object数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList中存储元素的数组
transient Object[] elementData;
// ArrayList中元素的个数
private int size;
// ...
}
ArrayList的构造方法
在创建ArrayList对象时,可以选择使用不同的构造方法来指定容量和初始元素。
ArrayList()构造方法
默认情况下,使用无参构造方法创建的ArrayList对象,容量为0,此时elementData指向一个空数组对象。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList(int initialCapacity)构造方法
如果需要指定ArrayList的容量大小,可以使用带参数的构造方法。在使用这个构造方法时,需要传入一个int类型的参数initialCapacity,表示ArrayList的初始容量。在实例化ArrayList对象时,elementData数组的长度将被初始化为initialCapacity。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 创建指定大小的Object数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 创建空Object数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList(Collection<? extends E> c)构造方法
除此之外,还可以使用带参数的构造方法,将一个已有的Collection集合转化为ArrayList。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// 如果elementData不是Object类型的数组,则将其转换为Object类型的数组
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
ArrayList的扩容机制
当向ArrayList中添加元素时,如果当前ArrayList的容量不足,就需要对ArrayList进行扩容。ArrayList的扩容机制是在底层数组elementData不足以存储所有元素时,将其扩容为原来的1.5倍
private void grow(int minCapacity) {
// oldCapacity是旧数组的长度
int oldCapacity = elementData.length;
// newCapacity是新数组的长度,为旧数组长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新数组的长度小于minCapacity,则将minCapacity作为新数组的长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新数组的长度大于MAX_ARRAY_SIZE,则将新数组的长度设置为Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将旧数组的元素复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
其中,MAX_ARRAY_SIZE是一个静态final变量,用来限制数组的最大长度。如果扩容后的数组长度超过MAX_ARRAY_SIZE,则会抛出OutOfMemoryError异常。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList的添加元素方法
当向ArrayList中添加元素时,需要使用add方法。add方法有两个重载版本:
public boolean add(E e) {
// 确保容量足够
ensureCapacityInternal(size + 1); // Increments modCount!!
// 添加元素
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将指定位置之后的元素后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将新元素插入指定位置
elementData[index] = element;
size++;
}
ensureCapacityInternal方法
在使用add方法添加元素之前,需要调用ensureCapacityInternal方法来确保ArrayList的容量足够存储新元素。如果ArrayList的容量不足,则会自动进行扩容。
private void ensureCapacityInternal(int minCapacity) {
// 判断当前数组长度是否小于minCapacity
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity方法
在ensureCapacityInternal方法中,会调用ensureExplicitCapacity方法,用于确保ArrayList的容量足够存储新元素。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果需要扩容,则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
add方法
在调用ensureCapacityInternal方法之后,add方法会将新元素添加到ArrayList中。如果是使用第二个重载版本的add方法,还需要将指定位置之后的元素后移一位。
public boolean add(E e) {
// 确保容量足够
ensureCapacityInternal(size + 1); // Increments modCount!!
// 添加元素
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将指定位置之后的元素后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将新元素插入指定位置
elementData[index] = element;
size++;
}
其中,rangeCheckForAdd方法用于检查指定位置是否超出ArrayList的范围。
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
ArrayList的删除元素方法
当从ArrayList中删除元素时,需要使用remove方法。remove方法有两个重载版本:
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;
}
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;
}
fastRemove方法
在remove方法的第一个重载版本中,会调用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
}
rangeCheck方法
在remove方法的第二个重载版本中,会调用rangeCheck方法来检查指定位置是否超出ArrayList的范围。
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
ArrayList的迭代器
当需要遍历ArrayList中的元素时,可以使用迭代器。ArrayList的迭代器实现了Iterator接口,提供了hasNext、next、remove方法,可以在遍历ArrayList时进行删除操作。
public class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个元素的索引
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings
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();
}
}
在迭代器的实现中,cursor表示下一个元素的索引,lastRet表示上一个元素的索引。checkForComodification方法用于检查迭代器是否遍历过程中被修改过。在调用迭代器的remove方法时,会调用ArrayList的remove方法来删除元素,并更新迭代器的状态。
总结
本文通过对ArrayList的源码分析,介绍了ArrayList的实现原理和常用方法的实现细节。ArrayList是Java中常用的集合类之一,其底层实现使用数组,支持快速随机访问和动态扩容,具有较高的性能。了解ArrayList的底层源码,可以帮助开发人员更好地理解Java集合类的实现原理,为实现高效的Java应用程序提供基础支持。