ArrayList动态扩容机制(长度可变原理)

ArrayList底层是数组结构的,数组的默认长度为10。当数组添加满了后,会自动扩容为1.5倍。

原理讲解:

1.用空参构造函数创建ArrayList集合容器。

  • 测试代码:
    • public class ArrayListDemo {
          public static void main(String[] args) {
              //创建ArrayList集合容器
              ArrayList<String> list = new ArrayList<>();
         
          }
      }
      
  • ArrayList部分源码:
    • //成员变量
      //ArrayList底层的数组,如果使用 ArrayList 的默认构造器时(默认构造器就是创建集合容器时不带参数),它的初始容量就是 0
      transient Object[] elementData;  
      //定义一个空数组,空参构造器返回该数组,以及扩容的时候用到
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      
      //空参构造函数
      public ArrayList() {
              this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
      }
      
    • 空参构造函数里把一个空数组赋值给集合底层的数组。

结论:如果只是创建了集合容器,没有进行过添加操作,底层数组的默认长度为0。

2.往集合容器里面添加一个元素

  • 测试代码:
    • public class ArrayListDemo {
          public static void main(String[] args) {
              ArrayList<String> list = new ArrayList<>();
      		
              //添加一个元素“张三”
              list.add("张三");
          }
      }
      
  • ArrayList部分源码:
    • //成员变量
      
      //ArrayList 的默认初始容量,如果没有显式指定容量,则使用这个值
      private static final int DEFAULT_CAPACITY = 10;
      //定义一个空数组,空参构造器返回该数组,以及扩容的时候用到
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      //size 是当前 ArrayList 中的元素数量,初始化为 0
      private int size; 
      //ArrayList底层的数组,如果使用 ArrayList 的默认构造器时(默认构造器就是创建集合容器时不带参数),它的初始容量就是 0
      transient Object[] elementData;  
      // 是一个常量,表示可以创建的最大数组大小
      //Integer.MAX_VALUE 是 2^31 - 1,即最大的正整数。减去 8 是为了留出一些空间,避免在某些情况下(如内存分配)出现溢出或其他问题
      private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
      
      ---------------------------------------------------------------------
      //add方法
          
      public boolean add(E e) {    //将元素 e 添加到 ArrayList 中
          	//这个方法用于确保 ArrayList 有足够的容量来存储新添加的元素
              ensureCapacityInternal(size + 1);  
          	// 会将元素 e 放到 elementData 数组的索引size位置(例如:添加第一个元素时,就是把元素放到0号索引位置),然后再把size的长度加1
              elementData[size++] = e;
              return true; //该方法总是返回true(异常除外),表示元素成功添加到ArrayList中
      }
      
      ---------------------------------------------------------------------
      //ensureCapacityInternal方法,add方法里面调用了该方法
      
      //minCapacity表示所需的最小容量
      private void ensureCapacityInternal(int minCapacity) {  
          	//ensureExplicitCapacity方法负责调整内部数组的实际容量
          
          	//calculateCapacity方法负责计算所需新容量
          	//elementData:当前数组
              ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
      }
      
      ----------------------------------------------------------------------
      //calculateCapacity方法,ensureCapacityInternal方法里调用了该方法
          
      //计算所需新容量
      private static int calculateCapacity(Object[] elementData,int minCapacity) {
          	//检查 elementData 是否为空的默认数组
              if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                  //如果是,返回默认容量和所需最小容量中的较大值
                  return Math.max(DEFAULT_CAPACITY, minCapacity);
              }
          	//否则,返回所需的最小容量
              return minCapacity;
      }
      
      -----------------------------------------------------------------------
      //ensureExplicitCapacity方法,ensureCapacityInternal方法里调用了该方法
          
      //调整内部数组的实际容量
      private void ensureExplicitCapacity(int minCapacity) {
          	//增加修改计数	
              modCount++;  //modCount是为了确保集合在被迭代时的安全性和一致性
      
              //如果所需容量大于当前数组长度,则调用 grow 方法
          	//即minCapacity如果大于当前数组的长度(elementData.length),则说明当前数组容量不足以容纳所需的最小容量,需要扩容
              if (minCapacity - elementData.length > 0)
                  grow(minCapacity); //调用 grow 方法来扩展数组的容量
      }
      
      -----------------------------------------------------------------------
       //grow方法,ensureExplicitCapacity方法里调用了该方法
          
       //扩展内部数组 elementData 的容量,以确保它能够容纳至少 minCapacity 个元素
      private void grow(int minCapacity) {
              int oldCapacity = elementData.length; // 获取旧容量(即元素的数量)
          
          	//利用位移运算符'>>'计算旧容量的 1.5 倍
          	//oldCapacity >> 1 : oldCapacity 的二进制向右移动一位,从而实现除以2的效果, 相当于oldCapacity/2,
              int newCapacity = oldCapacity + (oldCapacity >> 1); //计算新容量
          
          	//如果计算出的新容量小于所需的最小容量 minCapacity,则将新容量设置为 minCapacity
              if (newCapacity - minCapacity < 0)  //确保新容量满足最小需求:
                  newCapacity = minCapacity;
          
          	//检查新容量(newCapacity)是否超过了最大数组大小 MAX_ARRAY_SIZE
          	//如果超过,则调用 hugeCapacity 方法来处理这种情况,通常是为了避免内存溢出
              if (newCapacity - MAX_ARRAY_SIZE > 0)   //限制最大容量
                  newCapacity = hugeCapacity(minCapacity);
          
              // 使用 Arrays.copyOf 方法创建一个大小为 newCapacity的新数组,并将 elementData 中的所有元素复制到新数组中。elementData 引用被更新为指向这个新数组
              elementData = Arrays.copyOf(elementData, newCapacity);  //复制数组
      }
      
      ----------------------------------------------------------------------
      //hugeCapacity方法,grow方法里调用了该方法
          
      //这个方法用于处理非常大的数组容量
      private static int hugeCapacity(int minCapacity) {
          if (minCapacity < 0)  // 检查最小容量是否为负数
              throw new OutOfMemoryError();   // 如果是,抛出内存不足错误
          //三元表达式
          return (minCapacity > MAX_ARRAY_SIZE) ?  //如果所需容量超过最大数组大小
              Integer.MAX_VALUE :  // 如果是,就返回最大整数值,表示无法满足需求
              MAX_ARRAY_SIZE;      // 否则,返回最大数组大小
      }    
      

结论:

1.添加第一个元素是,底层会创建一个新的长度为10的数组
2.存满时,会扩容1.5倍

小结

  • 初始容量:
    • 如果使用 ArrayList 的默认空参构造器时,它的初始容量就是 0
    • 如果使用 ArrayList 的有参构造器时,它的初始容量就是你传入的参数 initialCapacity的值
  • 动态扩容大小:
    • 添加第一个元素是,底层会创建一个新的长度为10的数组
    • 存满时,会扩容1.5倍

你可能感兴趣的:(Java,java)