ArrayList源码解析
简介
特点:ArrayList是List接口的主要实现类,它存储有序的,可重复的数据。
优缺点:ArrayList线程不安全,但其查询、修改数据的效率高。
底层原理:ArrayList底层是基于数组来实现的。
Arraylist的继承与实现
ArrayList的类图(该图由Idea自动生成)
从上图可知,ArrayList继承了AbstractList,实现了Serializable、RandomAccess、Cloneable、List。
功能:
- 继承AbstractList类,实现了List接口。它是一个抽象类,实现了 List 的一些位置相关操作(比如 get,set,add,remove),是第一个实现随机访问方法的集合类,但不支持添加和替换。
- 实现Cloneable接口,重写了函数clone(),可以被克隆。
- 实现RandomAccess接口,提供了随机访问功能。
- 实现了Serializable接口,因此,ArrayList支持序列化,可以将对象转换为可保持或可传输的过程。
源码解析
本文将从ArrayList使用的角度解析源码
ArrayList属性
在阅读源码前,我们需要先理解一些ArrayList属性代表的含义
// 数组默认的初始容量为10
private static final int DEFAULT_CAPACITY = 10;
// 空的Object数组
private static final Object[] EMPTY_ELEMENTDATA = {
};
// 空的Object数组,将其与上面的区分开来,EMPTY_ELEMENTDATA用于有参构造函数且参数为0时,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA用于无参构造函数。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
// 集合中存放数据的对象,ArrayList的核心
transient Object[] elementData;
// 集合内数据的大小,size初始为0
private int size;
// modCount继承于AbstractList,用于记录数组的添加、删除操作次数
创建ArrayList对象
-
使用无参构造函数创建对象
ArrayList arr = new ArrayList();
在Idea中使用Ctrl+鼠标左键单击,可以进入ArrayList.java中查看源码。
// 可以看出,创建了一个elementData的长度为0的ArrayList对象 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
-
使用有参构造函数创建对象,参数的类型为int。
// 创建一个长度为initialCapacity的elementData数组 public ArrayList(int initialCapacity) { // 如果初始容量长度大于0,则从新创建一个Object数组 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; // 否则如果初始容量为0,则将EMPTY_ELEMENTDATA的地址赋给elementData。 } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { // 否则抛出异常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
-
使用有参构造函数创建对象,参数的类型为Collection。
// 使用这种方式创建ArrayList对象,会将Collection对象转为Object数组,然后将其拷贝给elementData数组 public ArrayList(Collection<? extends E> c) { // 将Collection对象转为数组 Object[] a = c.toArray(); // 将b数组的长度赋值给size,判断其是否不等于0 if ((size = a.length) != 0) { if (c.getClass() == ArrayList.class) { // 将a的地址赋给elementData elementData = a; } else { // 进行拷贝操作 elementData = Arrays.copyOf(a, size, Object[].class); } } else { // replace with empty array. elementData = EMPTY_ELEMENTDATA; } }
向arrayList集合中添加数据
1、add(E e)
add(E e)方法
功能:这个方法是将数据传入进来,按照数组自增的方式将其添加到数组中。
public boolean add(E e) {
// 判断集合内存储元素的个数加1看其是否可以承受这个容量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal(int minCapacity)方法
功能:确保数组内部的容量大小可以添加新的数据
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity(Object[] elementData, int minCapacity)方法
功能:比较minCapacity(size+1)与DEFAULT_CAPACITY(10)的大小,返回最大值。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ensureExplicitCapacity(int minCapacity)方法
功能:minCapacity,判断其大小是否大于数组的长度,如果大于就需要对数组进行扩容操作
modCount++:数组的修改次数+1,之后会讲到这个属性的功能。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow(int minCapacity)方法
功能:对数组进行扩容
private void grow(int minCapacity) {
// 将数组的长度定义为集合中旧的容量
int oldCapacity = elementData.length;
// 将新的容量定义为旧的容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新的容量小于旧的容量,就令新的容量等于旧的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新的容量减去最大数组长度(Integer.MAX_VALUE - 8),则令新的容量扩充为int类型的最大取值范围
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将旧的数组的数据复制到新的数组中去
elementData = Arrays.copyOf(elementData, newCapacity);
}
此时结束前面对数组的扩容判断之后,将新的数据添加到数组上。
elementData[size++] = e;
return true;
重点:集合在初始化时,它是一个空数组,只有当第一次添加数据时,它的长度才会变为10
2、add(int index, E element)
add(int index, E element)方法
功能:该方法的功能是向按照指定的位置,向集合中插入元素
public void add(int index, E element) {
// 用于add和addAll的范围检查,判断是否越界,否则抛出异常。
rangeCheckForAdd(index);
// 确保数组内部容量可以添加新的数据
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将elementData中从index开始的数据拷贝到elementData从index+1开始的位置。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将element元素添加到指定的index位置上
elementData[index] = element;
// 集合大小+1
size++;
}
3、addAll(Collection<? extends E> c)
功能:将collection集合中的数据添加到arrayList集合中
public boolean addAll(Collection<? extends E> c) {
// 将集合转为数组
Object[] a = c.toArray();
int numNew = a.length;
// 确保数组的容量可以添加这些数据,如果不行就扩容
ensureCapacityInternal(size + numNew); // Increments modCount
// 将a数组内的元素添加到elementData[size]之后.
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
移除arrayList集合中的数据
1、remove(int index)
功能:移除指定位置上的元素
public E remove(int index) {
// 查看index是否不在集合范围中
rangeCheck(index);
// 修改次数+1
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;
}
2、remove(Object o)
功能:移除指定元素
// 通过for循环遍历数组,获取需要删除元素的下标,通过remove删除。
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;
}
3、removeRange(int fromIndex, int toIndex)
功能:移除fromIndex到toIndex范围内的元素
protected void removeRange(int fromIndex, int toIndex) {
// 修改次数+1
modCount++;
// 需要向前移动的的长度
int numMoved = size - toIndex;
// 将toIndex后的元素向前移动numMoved
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
// 通过for循环置从newSize开始的元素为空
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
// 更新size的大小
size = newSize;
}
返回指定位置上的元素
get(index)
// 先进行范围检查,然后直接返回数组中下标为index的元素
public E get(int index) {
// index不能>=当前集合的大小
rangeCheck(index);
return elementData(index);
}
更新指定位置上的元素
set(int index, E element)
// 先进行范围检查,然后获取指定位置上的元素,然后存放到oldValue中,将需要更新的元素赋值给elementData[index],最后返回旧的数据。
public E set(int index, E element) {
// index不能>=当前集合的大小
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
判断集合中是否包含某一个元素
contains(Object o)
功能:判断集合中是否包含某一个元素
public boolean contains(Object o) {
// 判断下标是否大于等于0(表示元素存在)
return indexOf(o) >= 0;
}
indexOf(Object o)
功能:获取集合中存在的元素的下标
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;
}
// 返回-1表示元素不存在
return -1;
}
清空集合中全部的数据
public void clear() {
// 修改次数+1
modCount++;
// 使用for循环令数组中全部的元素都为空
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
注意:在删除或清空集合中的元素时,集合中的数据大小(size)发生改变,而数组的长度(length)不会发生改变。
获取集合内指定范围内的元素
subList(int fromIndex, int toIndex)方法
功能:获取集合内指定范围内的元素
public List<E> subList(int fromIndex, int toIndex) {
// 先进性范围检查,查看fromIndex、toIndex是否满足1条件
subListRangeCheck(fromIndex, toIndex, size);
// 返回一个新创建的SubList对象,将需要获取的范围传入其中。
return new SubList(this, 0, fromIndex, toIndex);
}
SubList类
介绍:它是ArrayList类里面的一个内部类,它的作用和ArrayList类的作用一样,它可以获取数组中指定位置的元素,而不是截取他们成为一个新的数组。
在SubList类中同样定义了修改、删除、添加、查询功能。
迭代器
iterator()方法
返回一个新创建的Itr对象
public Iterator<E> iterator() {
return new Itr();
}
Itr类
介绍:这个类和SubList类一样,也是一个内部类。Itr实现了Iterator接口。所以我们可以使用它进行遍历。
fail-fast机制
介绍:在ArrayList的使用中,可能会抛出一种异常,这种异常多数会出现在使用多线程的时候。
这个异常的名称为ConcurrentModificationException。
产生原因:在多个线程同时对集合进行操作时,某个线程对集合进行迭代,会将这个时间段的modCount赋值给Itr类中的expectedModCount属性,而其他线程在同一时间段对集合进行操作时modCount会发生更新,而expectedModCount不会进行更新,在迭代器中会有一个方法(checkForComodification())对modCount与expectedModCount的值进行比对,所以当二者的值不相等时,就会抛出异常。这就是fail-fast机制。
同时这也是为什么ArrayList集合不安全的原因。
fail-fast目的:为了防止读数据时产生线程安全问题而提出的机制,它是一个线程安全机制。
详情可以参考这位博主的帖子。
参考连接:
https://blog.csdn.net/zymx14/article/details/78394464
为了解决线程安全问题,我们可以使用CopyOnWriteArrayList代替ArrayList。