List底层源码解析

ArrayList 简介
ArrayList 的底层数据结构是一维数组,而且是动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList实例的容量。这可以减少递增式再分配的数量。
ArrayList 继承于 AbstractList ,实现了 List , RandomAccess , Cloneable , java.io.Serializable 这些接口。

ArrayList核心源码

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

   /**
    * 默认初始容量大小 
    */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组(用于空实例)。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

   //用于默认大小空实例的共享空数组实例。 
   //我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 保存ArrayList数据的数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains)
     * ArrayList 所包含的元素个数
     */
    private int size;

  /**
   * 带初始容量参数的构造函数(用户可以在创建ArrayList对象时自己指定集合的初始大小) 
   */
    public ArrayList(int initialCapacity) {
        //如果传入的参数大于0,创建initialCapacity大小的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //如果传入的参数等于0,创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //其他情况,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
    *默认无参构造函数 *DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0初始化为10,也就是说初始其实是空数   组当添加元素时数组容量初始化为10
    */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
   /**
    * 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。 
    */
    public ArrayList(Collection<? extends E> c) {
        //将指定集合转换为数组
        elementData = c.toArray();
        //如果elementData数组的长度不为0
        if ((size = elementData.length) != 0) {
            // 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的
            if (elementData.getClass() != Object[].class)
                //将原来不是Object类型的elementData数组的内容,赋值给新的Object类型的
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 其他情况,用空数组代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
   /**
    * 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化Arra 
    */
    public void trimToSize() {
       modCount++;
       if (size < elementData.length) {
           elementData = (size == 0)
             ? EMPTY_ELEMENTDATA
             : Arrays.copyOf(elementData, size);
        }
    }

arrayList扩容机制

ArrayList底层采用的是数组的数据结构,当数组已满时,需要扩容。ArrayList的扩容机制是在原数组长度的基础上增加50%的长度,即每次扩容后的长度为原长度的1.5倍。具体扩容过程如下:

  1. 当ArrayList中元素个数达到数组长度时,就需要进行扩容操作。
  2. 创建一个新的数组,长度为原数组长度的1.5倍。
  3. 将原数组中的元素复制到新数组中。
  4. 将新数组赋值给原数组。

这样就完成了一次扩容操作。由于扩容操作需要将原数组中的元素复制到新数组中,因此扩容操作的时间复杂度为O(n)。为了避免频繁扩容,ArrayList在创建时可以指定初始容量,如果能够预估元素个数,可以设置一个合适的初始容量,减少扩容次数,提高效率。


    1.   add()方法

/**
     * 将指定的元素追加到此列表的末尾。
     */
    public boolean add(E e) {
   //添加元素之前,先调用ensureCapacityInternal方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //这里看到ArrayList添加元素的实质就相当于为数组赋值
        elementData[size++] = e;
        return true;

  2.  ensureCapacityInternal() 方法

/得到最小扩容量
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
              // 获取默认的容量和传入参数的较大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

 当要add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。

  3.  ensureExplicitCapacity() 方法

//判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //调用grow方法进行扩容,调用此方法代表已经开始扩容了
            grow(minCapacity);
    }

        当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为10。minCapacity - elementData.length > 0 成立,所以会进入 grow(minCapacity) 方法。
        直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进入grow方法进行扩容

4. grow()方法

 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;
//如果新容量超过了最大限制 那么调用hugeCapacity(minCapacity)方法来计算更大的容量大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //通过调用Arrays.copyOf()方法将原有的数组复制到一个新的、更大的数组中,完成扩容操作
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

hugeCapacity() 方法 

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
       //对minCapacity和MAX_ARRAY_SIZE进行比较
        //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
        //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
        //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

关键点:

  grow方法会计算新的容量大小,一般是当前容量的1.5倍(如果超过了最大容量限制,则根据实际情况进行调整)。然后,通过调用Arrays.copyOf()方法将原有的数组复制到一个新的、更大的数组中,完成扩容操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值