声明
此部分内容,均为个人对ArrayList源码的理解,如有错误,可以通过评论或私聊指出。
简介
ArrayList类继承与实现如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
}
如上述代码所示,ArrayList继承了AbstractList类,实现了List、RandomAccess, Cloneable, java.io.Serializable
接口。
常规的数组在创建的时候需要指定数组大小,并且创建完成之后,数组大小是不能改变的。ArrayList数组的大小是可以
通过扩容机制进行自适应的改变,而且ArrayList提供了更多的方法供我们使用,能够进行有目的的增删改查操作,较之
普通数组,更加的灵活。
add()源码解析
add(E e)源码分析
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private static final int DEFAULT_CAPACITY = 10;
protected transient int modCount = 0; // 表示数组结构被修改的次数
/*---------------------------------------------------------------------------------------------------------------*/
public boolean add(E e) {
// 判断数组是否能够存放数据
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将数组e存入elementData数组中的size + 1位。
elementData[size++] = e;
// 返回true
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 判断elementData是否为空数组(如果在创建ArrayList的时候没有指定大小,elementData就会被初始化为空数组)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 判断此时数组中的大小是否比初始化容量大
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 调用ensureExplicitCapacity方法
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 数字结构修改次数+1
modCount++;
// overflow-conscious code
// 判断数组大小是否需要扩容
if (minCapacity - elementData.length > 0)
// 扩容
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
// 获取原长度
int oldCapacity = elementData.length;
// 计算新数组大小:通过位运算,可以提高运算效率
// 数组新长度为原长度的1.5倍大小
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断数组新长度是否比数组的大小要小
if (newCapacity - minCapacity < 0)
// 是,则让数组大小为新长度
newCapacity = minCapacity;
// 判断数组新长度是否比最大容量要大
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 获取数组新长度
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 设置slementData的新长度,扩容完成
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果数组实际大小比0小,则抛出OOM错误
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 判断数组实际大小是否超过限定最大值,是的话则返回int类型最大值,否则返回限定最大值
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
新增一个元素步骤如下:
第一步,会去判断数组的大小是否已经等于数组的容量,是的话,则进行扩容。
第二步,将数据存放到elementData[size++]中。
第三步,返回true。
add(int index, E element)源码分析
public void add(int index, E element) {
// 调用rangeCheckForAdd方法,检查index是合法
rangeCheckForAdd(index);
// 调用ensureCapacityInternal方法,检查是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 通过arraycopy方法,将elementData数组index索引开始的元素复制到elementData数组中index+1开始的位置,
// 一共复制size-index个元素
// 简单的说,就是将elemtnData数组中由index开始的元素,下标均+1
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将数据添加到elementData[index]中
elementData[index] = element;
// 数据长度+1
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
// index小于0或大于数组实际长度,则抛出错误
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 7, Size: 5
* at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:661)
* at java.util.ArrayList.add(ArrayList.java:473)
* at Demo.main(Demo.java:19)
*/
set(int index, E element)源码解析
public E set(int index, E element) {
// 判断index是否合法
rangeCheck(index);
// 获取原index的值
E oldValue = elementData(index);
// 替换原index的值
elementData[index] = element;
// 返回原index的值
return oldValue;
}
private void rangeCheck(int index) {
// 如果index大于等于数组长度,则抛出错误
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
remove()源码解析
remove(int index)源码解析
remove(int index)方法是根据下标删除数组元素。
public E remove(int index) {
// 判断index是否合法
rangeCheck(index);
// 数组结构被修改次数+1
modCount++;
// 获取下标为index的值
E oldValue = elementData(index);
// 判断删除索引的位置是否为最后一个位置
int numMoved = size - index - 1;
// 如果删除的位置不是最后一位
if (numMoved > 0)
// 通过arraycopy方法,将elementData数组index+1索引开始的元素复制到elementData数组中index开始
// 的位置,一共复制numMoved个元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将elementData中的最后一个元素置为null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
remove(Object o)源码解析
remove(Object o)是根据元素进行删除。多个相同元素值的元素,只会删除查询到的第一个元素。
public boolean remove(Object o) {
// 判断要删除的元素值是否为null
if (o == null) {
// 遍历elementData数组
for (int index = 0; index < size; index++)
// 判断对应下标的值是否为null
if (elementData[index] == null) {
// 调用fastRemove()方法,删除对应下标的元素
fastRemove(index);
// 返回true
return true;
}
} else { // 元素值不为null
// 遍历elementData数组
for (int index = 0; index < size; index++)
// 通过equals对要删除的元素以及对应下标的元素进行比较
if (o.equals(elementData[index])) {
// 调用fastRemove()方法,删除对应下标的元素
fastRemove(index);
// 返回true
return true;
}
}
// 如果查找不到该元素,则返回false
return false;
}
private void fastRemove(int index) {
// 数组结构被修改次数+1
modCount++;
// 判断删除索引的位置是否为最后一个位置
int numMoved = size - index - 1;
// 如果删除的位置不是最后一位
if (numMoved > 0)
// 通过arraycopy方法,将elementData数组index+1索引开始的元素复制到elementData数组中index开始的位
// 置,一共复制numMoved个元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将elementData中的最后一个元素置为null
elementData[--size] = null; // clear to let GC do its work
}
get()源码解析
get()源码如下:
public E get(int index) {
// 判断index是否合法,不合法则抛出错误
rangeCheck(index);
// index合法的话,返回elementData中对应下标的值
return elementData(index);
}
private void rangeCheck(int index) {
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
indexOf()源码解析
public int indexOf(Object o) {
// 判断查询的元素值是否为null
if (o == null) {
// 从elementData数组头开始遍历数组
for (int i = 0; i < size; i++)
// 找到第一个元素值为null的
if (elementData[i]==null)
// 返回该下标
return i;
} else {
// 从elementData数组头开始遍历数组
for (int i = 0; i < size; i++)
// 通过equals方法进行比较元素值,找到该元素
if (o.equals(elementData[i]))
// 返回该下标
return i;
}
// 如果不存在该元素则返回-1
return -1;
}
lastIndexOf()源码解析
public int lastIndexOf(Object o) {
// 判断查询的元素值是否为null
if (o == null) {
// 从elementData数组末尾进行遍历
for (int i = size-1; i >= 0; i--)
// 找到第一个元素值为null的
if (elementData[i]==null)
// 返回该下标
return i;
} else {
// 从elementData数组末尾进行遍历
for (int i = size-1; i >= 0; i--)
// 通过equals方法进行比较元素值,找到该元素
if (o.equals(elementData[i]))
// 返回该下标
return i;
}
// 如果不存在该元素则返回-1
return -1;
}
重点内容
ArrayList中,最精彩的部分在于扩容。通过oldCapacity + (oldCapacity >> 1);计算得出扩容后的数据大小为数组元大小的1.5倍。这里使用的是位运算,与oldCapacity + (oldCapacity / 1);相比,效率会更快。