以下的源码解析均基于jdk8
一、定义
- ArrayList是通过动态数组来实现的,与java数组不同的是它可以自动改变容量。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
- ArrayList不是线程安全的,其类似于Vector,只是Vector是线程安全的。
- ArrayList能快速随机搜索和设置元素,但在集合中间添加和删除元素的效率不高。
- ArrayList使用默认构造函数时添加元素后其初始容量为10,当容量不够容纳元素时其扩容方式是每次增加原来容量的1/2。
以下为ArrayList的继承结构图:
二、源码解析
1、属性
-
DEFAULT_CAPACITY :表示初始容量10
-
EMPTY_ELEMENTDATA :用于保存空集合的共享空数组
-
DEFAULTCAPACITY_EMPTY_ELEMENTDATA:用于保存空集合的共享空数组,如果使用默认构造函数创建,则使用该空数组
-
elementData:缓存集合元素的数组
-
size:记录集合大小的变量
-
modCount :记录当前集合被结构修改的次数,继承至AbstractList
private static final long serialVersionUID = 8683452581122892189L; /** * 初始容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 用于空实例的共享空数组实例 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 用于默认大小的空实例的共享空数组实例,如果使用默认构造函数创建,则使用该空数组实例 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存储ArrayList元素的数组缓冲区。ArrayList 的容量就是该数组缓存的长度。 * ArrayList 初始化时,elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * 当添加第一个元素后其容量扩展至默认容量也就是10 */ transient Object[] elementData; // non-private to simplify nested class access /** * ArrayList的大小 (包含元素的数量). * * @serial */ private int size; /** * 当前列表被结构修改的次数 * 结构修改指改变列表的大小或者迭代时的可能导致错误的增删改等操作 */ protected transient int modCount = 0;
2、构造函数
-
ArrayList:默认的构造函数,当不知道元素的大小时使用,其初始容量为10
-
ArrayList(int initialCapacity):构造指定容量的空列表
-
ArrayList(Collection<? extends E> c):可以将其他类型的集合比如HashSet转换成ArrayList集合
/** * 构造初始容量为10的空列表。 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 构造指定容量的空列表 * * @param initialCapacity 列表的初始容量 * @throws IllegalArgumentException 若传入的容量非法将抛出该异常 */ 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); } } /** * 构造包含指定元素的列表,其顺序为集合迭代的顺序 * * @param c 要将其元素放入此列表中的集合 * @throws NullPointerException 如果指定的集合为空 */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { //c.toArray方法可能出错,从而不会返回Object[] (参考 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 若集合长度为0则用空数组代替 this.elementData = EMPTY_ELEMENTDATA; } }
3、常用方法
3.1、trimToSize:将ArrayList的容量调整为当前列表的实际大小
/**
* 将ArrayList的容量调整为当前列表的实际大小。
* 可以通过该方法来最小化存储一个ArrayList实例
*/
public void trimToSize() {
//1.修改次数+1
modCount++;
if (size < elementData.length) {
//2.将多余的空元素去掉
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
3.2、ensureCapacity:根据所传的容量值给ArrayList实例扩容
扩容的主要步骤如下:
1.将所需的最小容量值与默认容量值比较,若大于默认容量值才进行扩容操作。
2.由于改变了集合的容量,所以将修改记录加一。
3.默认容量增加原本容量的1/2(oldCapacity >> 1右移一位表示原来的1/2)。当然扩容值有范围限定,为0-Integer.MAX_VALUE - 8之间。
/**
* 增加这个<tt>ArrayList</tt>实例的容量。
* 通过给定的最少的容量参数minCapacity来确保至少容纳的数量
*
* @param minCapacity 所需的最小容量
*/
public void ensureCapacity(int minCapacity) {
//1.给定待比较的容量值,若elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,
//则比较值为默认值10,否则为0
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
//2.当所传的容量值大于默认最小值,才进行扩容操作,调用ensureExplicitCapacity方法,
//然后调用 ensureExplicitCapacity方法
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//3.集合修改记录加1,并调用grow方法
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 要分配数组的最大大小。
* 请求的数组超过VM限制将导致OutOfMemoryError异常
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 增加容量,以确保它至少可以容纳由最小容量参数指定的元素数。
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//4.默认情况下,在原来容量的基础上增加1/2的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 5.根据新的容量扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
3.3、isEmpty:判断集合是否为空
/**
* 如果集合没有元素返回true
* @return
*/
public boolean isEmpty() {
return size == 0;
}
3.4、size:获取集合的元素数量
/**
* 返回集合的元素数量
*
* @return
*/
public int size() {
return size;
}
3.5、contains:集合是否存在某对象
/**
* 如果集合包含指定的元素则返回true
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
3.6、indexOf:获取集合第一次出现指定对象的索引(位置)
/**
* 返回集合中首次出现指定元素的索引,若不存在返回-1
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
3.7、lastIndexOf:获取集合最后一次出现指定对象的索引(位置)
/**
* 返回集合中最后出现指定元素的索引,若不存在返回-1
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
3.8、clone:获取集合的浅拷贝
浅拷贝:对于引用类型数据,只是拷贝内存地址,而不拷贝实例,而深拷贝即拷贝内存地址又拷贝实例。
/**
* 返回ArrayList实例的浅拷贝。 (元素本身没有被拷贝)
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
3.9、toArray:返回集合对应的数组
/**
* 按顺序返回集合中所有元素的数组
*返回一个新的数组,修改该数组不会影响原来的集合元素
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 返回ArrayList的模板数组
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
//1.若数组a的大小 < ArrayList的元素个数,则新建一个T[]数组,数组大小是“ArrayList的元素个数”,
//并将“ArrayList”全部拷贝到新数组中
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 2.若数组a的大小 > ArrayList的元素个数,则新建一个T[]数组,数组大小是“ArrayList的元素个数”,
// 则将ArrayList的全部元素都拷贝到数组a中
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
3.10、 get:获取集合指定位置的元素
/**
* 返回列表指定位置的元素
*/
public E get(int index) {
//检测是否越界
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
3.11、 set:设置集合指定位置的值
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
3.12、add:往集合中添加元素
/**
* 添加元素至集合的末尾
*/
public boolean add(E e) {
//将集合容量+1,修改记录也+1
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
/**
* 将指定的元素插入其中的指定位置,右边的元素依次往后移动,因此效率不是很高
*/
public void add(int index, E element) {
//1.检测下标边界
rangeCheckForAdd(index);
//2.将集合容量+1,修改记录也+1
ensureCapacityInternal(size + 1);
//3.将elementData数组index索引上的元素依次复制到index+1位置上,即依次往右移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//4.指定位置上设置当前元素,并将集合的元素数量+1
elementData[index] = element;
size++;
}
3.13、 remove(int index):移除指定位置的元素
remove(Object o):移除特定的元素
/**
* 移除集合中指定位置的元素,并返回原来的元素
* 右边的元素依次往左移动,影响效率
*/
public E remove(int index) {
//1.边界检查
rangeCheck(index);
//2.修改记录也+1
modCount++;
E oldValue = elementData(index);
//3.计算需要移动的元素数量,并将其向左移动一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//4.将最后一位元素置为空,接下来会自动进行垃圾回收
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 删除集合中第一次出现的指定元素,如果不存在就没有变化
*/
public boolean remove(Object o) {
//1.若指定元素为空,遍历集合找到第一次为空的元素删除即可
//2.若指定元素不为空,遍历集合找到第一次相等的元素然后删除
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;
}
/*
* 该方法与remove(int index)类似,只是没有边界检查,没有返回值
*/
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
}
3.14、clear:清空集合中的元素
/**
* 移除集合中的所有元素
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
3.15、addAll:添加子集合到集合中
/**
*将指定子集合中的所有元素按照顺序追加到该集合中
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/**
* 从指定位置将指定子集合中的所有元素按照顺序追加到该集合中
*
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
//1.算出要移动的元素数,将原来的集合index位置以后的元素都向右移动numNew(子集合c的元素数)个位置
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//2.将子集合c的所有元素从index处开始拷贝至该集合中
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
参考链接:
https://blog.csdn.net/fighterandknight/article/details/61240861
https://www.cnblogs.com/skywang12345/p/3308556.html#a6