ArrayList源码解析
ArrayList的类结构图
ArrayList 是 java 集合框架中比较常用的用于存储单列数据的容器。它继承自 AbstractList,实现了 List 接口,同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 支持快速访问、复制、序列化。ArrayList底层基于数组实现,容量大小动态可以变化。
ArrayList继承了AbstractList类并实现类List接口,所以ArrayList具有了AbstractList和List的功能。而AbstractList内部已经实现了获取Iterator和ListIterator的方法。所
ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问。
ArrayList实现了Cloneable接口,表明ArrayList支持克隆。
ArrayList实现了Serializable接口,表明ArrayList支持序列,可以将ArrayList以流的形式通过ObjectInputStream/ObjectOutputStream来写/读。
ArrayList底层是如何实现的?
ArrayList 是通过基于数组的方式实现的动态数组。
在 ArrayList 内部,有一个对象数组(elementData)用于存储元素。当创建 ArrayList 对象时,会初始化一个初始容量的数组。随着元素的不断添加,ArrayList 会根据需要自动进行扩容。
当添加元素时,ArrayList 会检查当前数组容量是否足够,如果不够,则会创建一个新的更大容量的数组,并将原有元素复制到新数组中。这个过程称为扩容。通过这种方式,ArrayList 实现了动态调整数组容量。
在进行删除操作时,ArrayList 会将删除位置后面的元素向前移动一个位置,以填补被删除元素的空缺。同时,ArrayList 会判断是否需要缩小数组容量,如果数组容量过大且元素数量远小于容量的 1/4,则会进行缩容操作,减少内存占用。
由于 ArrayList 的底层采用数组实现,所以随机访问元素的时间复杂度为 O(1),即常数时间。但是在进行插入和删除操作时,为了保持数组的连续性,可能需要移动大量元素,时间复杂度为 O(n),其中 n 是元素的数量。因此,ArrayList 适用于对随机访问的需求较多,而对插入和删除操作效率要求不高的场景。
ArrayList的底层使用数组实现的源码如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//存储ArrayList元素的数组缓冲区。 ArrayList的容量就是这个数组缓冲区的长度。任何 empty ArrayList with //elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 将在添加第一个元素时扩展为DEFAULT_CAPACITY。
transient Object[] elementData;
....
}
注意,本文的源码为JDK18
ArrayList的初始化
ArrayList会在构造函数执行的时候初始化,ArrayList有三种构造函数,分别为:
- 带初始化容量大小参数(initialCapacity)的构造函数
- 无参构造函数,默认会初始化容量大小为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA的值为10,所以默认的初始化大小为10;这种最常用。
- 带有Collection参数的构造函数,会将Collection的元素Copy到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() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
添加元素
方法名 | 描述 |
---|---|
public boolean add(E e) | 添加元素 |
public void add(int index, E element) | 在制定位置添加元素 |
public boolean addAll(Collection<? extends E> c) | 将Collection的全部元素添加到集合中 |
public boolean addAll(int index, Collection<? extends E> c) | 将Collection的按照index位置开始的所有元素添加到集合中 |
添加元素的源码如下:
public boolean add(E e) {
modCount++;
add(e, elementData, size);//将元素添加到size的位置
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
//index的值和elementData的长度相等,则扩容
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
//将元素的放到索引位置
elementData[index] = element;
//容器的size+1
size = s + 1;
}
扩容
扩容是在grow()函数中进行,它返回的是一个新的Object[] 数组,新数组已经将就数据拷贝。
- 如果elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA(空数组),并且元素小于DEFAULT_CAPACITY(10 个),则对空数组进行初始化
- 如果不为空数组,则进行初始化,正常情况下新的数组大小是旧数组的1.5倍
- 如果超出了Interger的软最大值则报错
- 最多返回Interger的软最大值( SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8)
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
//对空数组进行初始化
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
private static int hugeLength(int oldLength, int minGrowth) {
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
return SOFT_MAX_ARRAY_LENGTH;
} else {
return minLength;
}
}
删除元素
remove(Object o)
方法:该方法用于删除指定的元素。
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
- 首先,
remove(Object o)
方法会判断是否删除的是null
值,然后遍历 ArrayList 查找匹配的元素。 - 如果找到匹配的元素,会调用
fastRemove(int index)
方法进行快速删除,该方法将需要删除元素的位置(index)后面的元素向前移动,覆盖需要删除的元素。 - 最后,将
size
赋值为newSize,newSize实际上是减一了,表示 ArrayList 的元素总数减少一个。 - 并将最后一个位置上的元素清空。
查找元素
get(int index)用于获取指定位置的元素,如果index超出长度,则报IndexOutOfBoundsException异常。查找指定位置元素的时间复杂度为O(1)。
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
contains(Object o)
方法:该方法用于判断 ArrayList 是否包含指定的元素。
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
contains(Object o)
方法内部调用了 indexOf(Object o)
方法
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i] == null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
indexOf(Object o)
方法首先判断要查找的元素是否为null
,如果是,则遍历 ArrayList 的元素,找到第一个值为null
的元素,返回其索引值。- 如果要查找的元素不为
null
,则遍历 ArrayList 的元素,调用元素的equals
方法进行比较,找到第一个相等的元素,返回其索引值。 - 如果未找到指定元素,则返回 -1。
迭代器源码解析
ArrayList 的迭代器(Iterator)是通过内部类 Itr
来实现的,以下是对其源码的解析:
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个元素的索引
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification(); // 检查 ArrayList 是否被修改过
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(); // 检查 ArrayList 是否被修改过
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();
}
}
Itr
类实现了Iterator<E>
接口,通过实现hasNext()
、next()
、remove()
和checkForComodification()
方法来支持迭代。cursor
属性表示下一个元素的索引,lastRet
属性表示上一个元素的索引,expectedModCount
属性表示预期的修改次数。hasNext()
方法用于判断是否还有下一个元素,即判断cursor
是否等于size
。next()
方法用于获取下一个元素,首先调用checkForComodification()
方法检查 ArrayList 是否被修改过,然后根据cursor
获取下一个元素并返回,同时更新lastRet
和cursor
的值。remove()
方法用于移除上一个元素,首先检查lastRet
是否小于 0,如果是则抛出异常,然后再次调用checkForComodification()
方法检查 ArrayList 是否被修改过,在通过ArrayList.this.remove(lastRet)
方法移除元素后,更新cursor
和lastRet
的值,并将expectedModCount
修改为当前的modCount
。checkForComodification()
方法用于检查 ArrayList 是否被修改过,即判断modCount
是否等于expectedModCount
,如果不相等则抛出异常。
ArrayList 的迭代器通过内部类 Itr
实现,支持对集合进行顺序迭代,并支持在迭代过程中通过迭代器的 remove()
方法删除元素。迭代器在每次访问元素之前会检查 ArrayList 是否被修改过,利用 modCount
和 expectedModCount
来实现快速失败机制,保证在迭代过程中对集合的修改不会产生错误的结果。
需要注意的是,在使用迭代器遍历 ArrayList 的过程中,不建议直接使用 ArrayList
对象的 remove()
方法进行元素的删除操作,而应该使用迭代器的 remove()
方法进行删除,以避免产生并发修改异常。