Java的ArrayList底层原理
在Java中,ArrayList是List接口的一个典型实现。它是一种动态数组,可以根据需要增长或缩小其容量。在内部,ArrayList使用一个数组来存储元素,这个数组的大小会根据需要动态调整。
1、 内部结构
1.1. 成员变量
ArrayList的内部结构主要由以下几个成员变量组成:
elementData:一个Object类型的数组,用于存储ArrayList中的元素。
size:一个int类型的变量,表示当前ArrayList中元素的数量。
1.2 构造方法
ArrayList提供了多个构造方法,其中最常用的是无参构造方法,它会创建一个初始容量为0的ArrayList。其他构造方法可以指定初始容量,例如:
ArrayList<String> list = new ArrayList<>(10); // 创建一个初始容量为10的ArrayList
1.3扩容机制
当ArrayList的元素数量达到其容量时,ArrayList会自动扩容。扩容的方式如下:
如果当前容量小于64,则新容量为当前容量的两倍加2。
如果当前容量大于或等于64,则新容量为当前容量的1.5倍。
1.4.添加和删除操作
在ArrayList中,添加和删除操作的时间复杂度为O(1)。具体实现如下:
添加操作:如果当前容量足够,则直接将元素添加到数组末尾;否则,先扩容,再将元素添加到数组末尾。
删除操作:直接将要删除的元素后面的所有元素向前移动一位。
1.5性能分析
ArrayList的性能主要取决于其内部数组的扩容机制。在大多数情况下,ArrayList的性能表现良好,但在极端情况下,可能会出现频繁的扩容操作,导致性能下降。因此,在使用ArrayList时,应该尽可能避免频繁的扩容操作。
2、源码分析
2.1成员变量
transient Object[] elementData; // 存储元素的数组
private int size; // 当前元素数量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 数组的最大容量
private static final Object[] EMPTY_ELEMENTDATA = {}; // 空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认容量的空数组
2.2 构造方法
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 (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size(), Object[].class);
size = elementData.length;
}
2.3扩容机制
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);
}
2.4添加和删除操作
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
3、具体执行过程如下
第一次添加数据时:
添加第11个数据时就要进行扩容了:
4、总结
ArrayList 的源码实现主要包括成员变量、构造方法、扩容机制、添加和删除操作等部分。通过这些部分的实现,ArrayList 能够高效地存储和操作数据。在实际使用中,我们需要根据实际需求选择合适的容量,并尽量避免频繁的扩容操作,以提高程序的性能。