ArrayList
java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
ArrayList的三种构造方法
ArrayList的扩容机制(源码)
/**
* ArrayList主要成员变量
*/
private static final int DEFAULT_CAPACITY = 10;
// 定义一个空数组,用来判断ArrayList第一次添加数据的时候要扩容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 数组的容量,第一次添加数据时,容量扩为10
transient Object[] elementData;
// 当前数组的长度
private int size;
/**
* 构造一个初始容量为10的空列表。
*/
public ArrayList() {
Javathis.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 共享的空数组实例,用于默认大小的空实例(通过无参构造器构造出来的是长度为0的数组)
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
构造出来的是长度为0的数组,但初始容量为10,为什么?(通过扩容实现)
以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。
1、add()
/**
* 将指定的元素追加到此列表的末尾
* JDK11将add方法进行了简化
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 增加modCount!!
elementData[size++] = e;
// 只要调用方法就一定返回true
return true;
}
2、ensureCapacityInternal()
/**
* 在使用add方法前,先调用了ensureCapacityInternal方法
* 获取最小容量
*/
private void ensureCapacityInternal(int minCapacity) {
// add 第1个元素时,minCapacity=1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 通过Math.max()方法比较后,minCapacity=10(初始容量变为10)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
3、ensureExplicitCapacity()
/**
* 判断是否需要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// ArrayList扩容方法
grow(minCapacity);
}
- 当 add 第1个元素时,无参构造器构造出来的是长度为0的数组,即 elementData.length = 0;此时minCapaucity = 10,那么 minCapacity - elementData.length > 0 会返回true,执行grow()方法。
- 当 add 第2个元素时,minCapacity = 2,此时的 elementData.length 在添加第一个元素后执行完group()方法,容量变成 10 。那么 minCapacity - elementData.length > 0 返回false,不扩容。
- 目前的初始容量为10,此后一直添加至元素个数超过10,那么 minCapacity - elementData.length > 0 成立,再次调用grow()方法进行新的扩容。
4、grow()
/**
* 分配的最大数组大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* ArrayList扩容方法
* 增加容量以确保它至少可以容纳最小容量参数指定的元素个数。
*/
private void grow(int minCapacity) {
// overflow-conscious code
// 首先获取数组的旧长度(即存满时的数组长度)
int oldCapacity = elementData.length;
// 二进制中,将 oldCapacity 右移一位,其效果相当于 oldCapacity/2,即新容量变为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断新容量是否大于需要的最小容量,如果小于,则返回 true,那么新容量就替换为所需要的最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 判断新容量是否大于 MAX_ARRAY_SIZE,如果成立,调用 hugeCapacity(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);
}
5、hugeCapacity()
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
// 内存溢出
throw new OutOfMemoryError();
// 对 minCapacity 和 MAX_ARRAY_SIZE 进行比较(即判断 Integer.MAX_VALUE - minCapacity < 8)
// 若 minCapacity大,Integer.MAX_VALUE则作为新数组的大小
// 若 MAX_ARRAY_SIZE大,MAX_ARRAY_SIZE则作为新数组的大小
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}