List集合源码解析

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;
    }
  1. 使用ArrayList集合添加数据时,因为ArrayList集合底层基于数组实现,由ExtArrayList()构造函数所知,ArrayList集合默认初始化为0,所以需要对数组进行扩容,即ensureCapacityInternal(size + 1);的执行。
  2. 进入ensureCapacityInternal(size + 1);后,代码如下所示: 由于源码还是在调用方法所以进入calculateCapacity(elementData, minCapacity),查看这个方法到底想要达到什么目的.
    private void ensureCapacityInternal(int minCapacity){
    
    
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  1. 进入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;
    }
  1. 由第三步可知,可以获取到数组的容量,作为ensureExplicitCapacity()参数,进行判断 。
    private void ensureCapacityInternal(int minCapacity){
    
    
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  1. 进入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);
        }
    }
  1. 如果数组不需要扩容是,则无需执行第六步;否则,需对数组进行扩容,以便存放更多数据。
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的区别

相同点
底层都是使用数组实现的
不同点

  1. 默认初始化时
    • ArrayList集合默认不会初始化,第一次调用add()方法时才会初始化
    • Vector集合默认初始化为0
  2. 数组扩容
    • ArrayList集合底层数组扩容默认为原来的50%
    • Vector集合底层数组扩容默认为原来的100%
  3. 线程安全
    • ArrayList 线程不安全
    • Vector 线程安全

代码地址

https://github.com/memo012/list-source-study

公众号

希望大家多多关注,里面不定期发放干货
领取全套资料:回复关键字【666】
迈莫公众号

猜你喜欢

转载自blog.csdn.net/qq_41066066/article/details/103515112