List的主要实现类,底层使用Object[]存储,适用于频繁的查找工作,线程不安全。
特点:
1.增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置;
2.查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。
【数据结构】ArrayList底层是数组队列,相当于动态数组。
源码分析:
①成员变量:
private static final int DEFAULT_CAPACITY = 10;// 默认的初始容量是10
// 空元素数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认容量的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储元素的数组
transient Object[] elementData;
// 集合容量
private int size;
②构造方法:
// 1.构造一个初始容量的空数组。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 2.构造具有指定初始容量的数组。
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);
}
}
//3.构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序
public ArrayList(Collection<? extends E> c) {
// 将集合构造中的集合对象转成数组
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
// 类型为ArrayList则直接赋值
elementData = a;
} else {
//如果不一样,使用Arrays的copyOf方法进行元素的拷贝
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
③扩容源码:
// 添加一个元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 1.主体函数
private void ensureCapacityInternal(int minCapacity) {
// 对容量进行判断
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 2.通过最小容量和默认容量求出较大值 (主要用于第一次扩容:如果是无参构造方式创建的数组对象,第一次添加元素的时候扩容到大小为10)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 3.判断是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {
// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)
modCount++;
// 判断当前最小容量是否大于数组长度
if (minCapacity - elementData.length > 0)
// 将计算出来的容量传递给核心扩容方法
grow(minCapacity);
}
// 4.核心扩容方法
private void grow(int minCapacity) {
// 记录数组的实际长度
int oldCapacity = elementData.length;
// 核心扩容算法,原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断新容量是否小于当前最小容量(第一次调用add方法必然小于)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 判断新容量是否大于最大数组长度,如果
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 条件满足就计算出一个超大容量
newCapacity = hugeCapacity(minCapacity);
// 调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结:
初始容量:创建ArrayList对象时,会分配一个初始容量,默认为10。
增长因子:每次扩容时,ArrayList会根据增长因子来计算新的容量。默认情况下,增长因子为1.5,即每次扩容后容量变为原来的1.5倍。
扩容触发条件:当ArrayList的size超过当前容量时,就会触发扩容操作。
扩容策略:ArrayList在扩容时,会创建一个新的更大容量的数组,并将原有元素复制到新数组中。具体的扩容流程如下:
当向ArrayList添加元素时,会先检查当前容量是否足够。如果不足,则进行扩容操作。
扩容时,根据增长因子计算新的容量,并创建一个新的数组。
将原有数组中的元素复制到新数组中。
更新ArrayList内部的引用,指向新数组。
添加新元素到新数组中。