List集合源码解析
由图所知,List接口实现类有ArrayList,Vector,LinkedList,其中ArrayList和LinkedList最为常用,下面着重介绍这个实现的源码
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
由图可知,ArrayList集合实现了List,首先咱们先从add()方法入手源码。
@Override
public boolean add(E e) {
// 对数组实现扩容
ensureCapacityInternal(size + 1);
// 对我们的数据的元素赋值
elementData[size++] = e;
return true;
}
/**
* elementData数据存放我们ArrayList所有的数据
* transient 非序列化
*/
transient Object[] elementData;
/**
* 给我们的数组容量赋值空
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ExtArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 使用ArrayList集合添加数据时,因为ArrayList集合底层基于数组实现,由ExtArrayList()构造函数所知,ArrayList集合默认初始化为0,所以需要对数组进行扩容,即
ensureCapacityInternal(size + 1);
的执行。 - 进入
ensureCapacityInternal(size + 1);
后,代码如下所示: 由于源码还是在调用方法所以进入calculateCapacity(elementData, minCapacity)
,查看这个方法到底想要达到什么目的.
private void ensureCapacityInternal(int minCapacity){
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- 进入
calculateCapacity(Object[] elementData, int minCapacity)
方法后,判断添加元素时,是否是第一次添加元素,如果是,则走if语句,返回默认扩容容量,因为当第一次添加元素时,参数minCapacity
的值为1,而DEFAULT_CAPACITY
的值为10,所以会返回给DEFAULT_CAPACITY
值;如果不是第一次添加,则直接返回minCapacity
值。相当于这个方法的作用来给数组确定容量。
/**
* 给我们的数组容量赋值空
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 数组默认容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 给数组确定容量
* @param elementData 数组
* @param minCapacity
* @return
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 添加元素时, 如果我们数组是为空走if语句(第一次)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- 由第三步可知,可以获取到数组的容量,作为
ensureExplicitCapacity()
参数,进行判断 。
private void ensureCapacityInternal(int minCapacity){
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- 进入
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))
中,判断数组中是否需要继续扩容,若需要,则执行grow(minCapacity);
方法.
/**
例如: 你执行如下代码,当你执行到第十行代码时,由于你先前数组扩容容量为10,
而你现在又要添加一条新的数组,很明显,11>10,minCapacity = 11,elementData.length = 10,
minCapacity - elementData.length > 0,需要进行扩容,来存放数据。
**/
public class Main {
public static void main(String[] args){
ExtArrayList<String> list = new ExtArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("33--"+i);
}
list.add("34");
}
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++ ;// 线程安全
// 相当于判断我们数组中是否需要继续扩容
if (minCapacity - elementData.length > 0) {
// 对我们的数组实现扩容
grow(minCapacity);
}
}
- 如果数组不需要扩容是,则无需执行第六步;否则,需对数组进行扩容,以便存放更多数据。
private void grow(int minCapacity) {
// 获取原来的数组长度
int oldCapacity = elementData.length;
// 获取新的数组长度 oldCapacity >> 1 表示 oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 第一次执行
if (newCapacity - minCapacity < 0){
// 默认长度为10
newCapacity = minCapacity;
}
// 如果我们的扩容长度大于Integer 最大值的情况下
// 限制我们数组扩容最大值
if (newCapacity - MAX_ARRAY_SIZE > 0){
newCapacity = hugeCapacity(minCapacity);
}
// 对我们的数组进行扩容 将旧的数组数据复制到新的数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
get()方法
@Override
public E get(int index) {
// 查询数据
return (E)elementData[index];
}
通过下标来进行过去即可。
remove()方法
public E remove(int index){
// 检查我们的下标是否越界
rangeCheck(index);
// 获取要删除对象
E oldValue = elementData(index);
// 计算移动位置
int numMoved = size - index - 1;
// 判断如果删除数据的时候,不是最后一个的情况下,将删除后面的数据都往前移一位
if (numMoved > 0){
/*
* 第一个参数: 源数组
* 第二个参数: 源数组要复制的起始位置
* 第三个参数: 目的数组
* 第四个参数: 目的数组存放的起始位置
* 第五个参数: 复制的长度
*/
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
}
// 如果numMoved为0的情况下,说明后面不需要往前移动,直接将最后一条数据赋值为null
elementData[--size] = null;
return oldValue;
}
- 至此,ArrayList集合的核心功能源码已分析完毕.
Vector集合
因为Vector集合和ArrayList集合实现原理类似,所以在这儿就不讲解其源码实现原理。
Vector和ArrayList的区别
相同点
底层都是使用数组实现的
不同点
- 默认初始化时
- ArrayList集合默认不会初始化,第一次调用add()方法时才会初始化
- Vector集合默认初始化为0
- 数组扩容
- ArrayList集合底层数组扩容默认为原来的50%
- Vector集合底层数组扩容默认为原来的100%
- 线程安全
- ArrayList 线程不安全
- Vector 线程安全
代码地址
https://github.com/memo012/list-source-study
公众号
希望大家多多关注,里面不定期发放干货
领取全套资料:回复关键字【666】