集合框架图:

2023年7月14日,ArrayList_第1张图片

集合和数组的区别

2023年7月14日,ArrayList_第2张图片

2023年7月14日,ArrayList_第3张图片

AarrayList

ArrayList底层实现原理

ArrayList的底层实现是基于数组的动态扩容。

  1. 初始容量:当创建一个新的ArrayList对象时,它会分配一个初始容量为10的数组。这个初始容量可以根据需求进行调整。
//表示默认的初始容量,该值被设置为10
private static final int DEFAULT_CAPACITY = 10;
//表示一个空数组,即一个长度为0的Object数组。
private static final Object[] EMPTY_ELEMENTDATA = {};
//表示具有默认初始容量的空数组。在无参构造函数中使用这个数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//该数组用于存储ArrayList的元素。它是ArrayList的核心数据结构。
   transient Object[] elementData; 
//表示ArrayList当前包含的元素数量。
   private int size;
//构造一个具有指定初始容量的ArrayList。如果指定的初始容量大于0,将创建一个具有此大小的新数组;如果初始容量为0,则使用空数组EMPTY_ELEMENTDATA;否则,抛出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);
       }
   }
//构造一个默认初始容量的ArrayList。使用数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA作为初始数组。
   public ArrayList() {
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }
  1. 添加元素:当使用ArrayList的add(E element)方法添加一个元素时,它会先检查数组是否已满。如果数组已满,ArrayList会执行以下操作:
  1. 创建一个新的容量更大的数组,默认情况下是原数组容量的1.5倍(在Java 7之前是原数组容量的两倍)。例如,如果原数组的容量是10,那么新数组的容量将是15。
  2. 将原数组中的元素复制到新数组中。
  3. 更新ArrayList的内部数组引用为新数组。
  4. 更新ArrayList的内部数组引用为新数组。
private void grow(int minCapacity) {
  		//获取当前 ArrayList 的容量
        int oldCapacity = elementData.length;
        //计算新的容量,即将原始容量的一半加到原始容量上,相当于扩容 50%。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //检查新容量是否小于所需的最小容量,如果是则将最小容量设为新容量。这个检查主要用于确保扩容后的容量足以容纳要添加的元素。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //检查新容量是否超过了 MAX_ARRAY_SIZE,如果是则调用 hugeCapacity(minCapacity) 方法获取一个巨大容量。MAX_ARRAY_SIZE 是 Java 数组的最大容量限制。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //将 elementData 数组扩容为新的容量。利用 Arrays.copyOf() 方法可以方便地将数组扩容并复制原始元素到新数组中。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  1. 删除元素:当使用ArrayList的remove(int index)方法删除一个元素时,ArrayList会执行以下操作:
  1. 将要删除的元素之后的所有元素向前移动一个位置,以覆盖被删除的元素。
  2. 更新ArrayList的内部数组长度。
public E remove(int index) {
    //校验索引是否越界,如果索引值小于0或者大于等于当前列表的大小 size,则抛出 IndexOutOfBoundsException 异常。
        rangeCheck(index);
//(用于记录结构性修改次数的计数器)加1,表示对 ArrayList 进行了一次修改。
        modCount++;
    //调用 elementData(index) 方法获取要删除的元素的值,将其保存为 oldValue。
        E oldValue = elementData(index);
//计算需要移动的元素的个数,即从待删除元素的下一个位置到列表末尾的元素个数。
        int numMoved = size - index - 1;
    //检查是否需要移动元素。如果 numMoved 大于 0,表示待删除的元素不是列表的最后一个元素,需要将后面的元素往前移动,以填补删除元素的空缺。
        if (numMoved > 0)
            //使用 System.arraycopy() 方法将后面的元素往前移动。从 elementData 数组的 index+1 位置开始,复制 numMoved 个元素到 elementData 数组的 index 位置,覆盖待删除元素。
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
    //将列表中最后一个位置的元素设置为 null,以便让垃圾回收机制回收。同时,将列表的大小 size 减1,相当于删除了一个元素。
        elementData[--size] = null; 
//返回被删除的元素
        return oldValue;
    }
  1. 获取元素:当使用ArrayList的get(int index)方法获取一个元素时,ArrayList会直接通过索引访问数组中的对应元素。
E elementData(int index) {
        return (E) elementData[index];
    }
  1. 内存管理:当ArrayList执行扩容或缩容操作时,它会分配新的内存来存储更大或更小的数组,然后将旧数组中的元素复制到新数组中。这意味着在扩容或缩容过程中会涉及到内存的分配和复制操作,可能会带来一定的性能开销。