概述
ArrayList继承自AbstractList,实现了List接口,本质上是一个默认初始长度为10,且支持自动扩容的数组。ArrayList非线程安全,多线程环境下操作需要开发人员保证线程安全
关键变量和常量
// 数组初始容量
private static final int DEFAULT_CAPACITY = 10;
// 数组容器,用于存放ArrayList元素.
transient Object[] elementData;
初始化
ArrayList提供三种初始化方法
- 不传参
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
使用该方式创建ArrayList时,elementData
初始化为空数组,使用DEFAULT_CAPACITY = 10
作为初始容量
- 传入初始容量
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
初始化时传入初始容量,并将elementData
初始化为对应长度的数组,传入0时和第一种初始化方法相同,传入小于0的容量时,抛出IllegalArgumentException
异常
- 传入集合
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
该方法很好理解,就是将传入的集合转换成数组,并赋值给elementData
, 关键在于if (elementData.getClass() != Object[].class)
这行代码,加这个判断的意图是什么?注释的含义是c.toArray()
返回的不一定是Object[]类型,这里需要了解一下JDK-6260652 Bug
举个例子,使用如下代码创建一个ArrayList
List
Arrays.asList(new String[]{"123"}).toArray().getClass()
返回的是String[]
类型,而非Object[]
类型,如果再执行objs[0] = new Object();
,会抛出java.lang.ArrayStoreException
异常。
具体原因要看Arrays#ArrayList.toArray()
方法
public T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
如上代码所示,Arrays#ArrayList.toArray()
返回的是T[]
类型,如果传入String
数组,则返回的数组类型是String[]
类型。对于一个String[]
类型数组,如果赋一个Object
的值,显然会出错。
因此才会有上面那个判断,判断是否为Object[]
类型,如果不是,将数组转换为Object[]
类型,避免后续赋值时出现java.lang.ArrayStoreException
异常
获取
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
从ArrayList
根据下标获取元素时,首先判断index
是否越界,如果越界,抛出IndexOutOfBoundsException
异常,否则返回对应下标的元素
添加
添加元素到ArrayList有两个方法
- 添加元素到指定位置
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++;
}
// 检查index是否越界
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算数组所需的容量,如果elementData为空数组,则从DEFAULT_CAPACITY, minCapacity取较大值返回
// 否则返回传入的minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 判断数组是否需要扩容,如需要,调用grow方法扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容数组。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 右移一位,即原有基础上扩容50%
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);
}
首先检查index
是否越界,越界抛出IndexOutOfBoundsException
异常,然后判断数组是否需要扩容,如需扩容,扩容后将元素添加到指定index
,具体请看代码注释
- 添加元素到末尾
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 = 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;
}
常规操作,第一步先判断是否越界。此处modCount
变量用于统计ArrayList
发生结构变化的次数,如果modCount
的值与预期不相符,会抛出ConcurrentModificationException
异常,一般出现在遍历ArrayList
时,删除或添加元素的场景。
然后获取对应index
的值,计算要移动的元素数量,如果numMoved > 0
,则通过System.arraycopy
,将index + 1
之后的numMoved
个元素拷贝到index
位置;如果numMoved == 0
,直接赋值为null
,等待GC回收
- 删除第一个查找到的对象
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;
}
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
}
该方法传入一个对象,遍历找到ArrayList
中对应的第一个对象后,将其删除,流程和上面一个方法差不多
替换
替换指定位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
首先判断index
是否越界,越界抛出IndexOutOfBoundsException
异常,未越界则从elementData
中获取对应index
的值,并将对应index
的值设置为新值,然后返回oldValue
modCount
用于记录数组结构变更次数,每一次添加或删除元素时,都会+1.在使用迭代器遍历时,如果modCount
和预期不一致,会抛出ConcurrentModificationException
异常