• ArrayList的扩容机制原理及源码分析


    1.初始化ArrayList无参构造器

    1.1 使用无参构造器创建ArrayList对象

    List list = new ArrayList();

        源码分析:调用无参构造器,创建了一个空的elementData数组,其初始化值为{}。

    1. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    2. public ArrayList() {
    3. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    4. }

    1.2 首次调用add(E e)方法添加数据

    1. //首次扩容
    2. for (int i = 1; i <=10 ; i++) {
    3. list.add(i);
    4. }
    5. //再次扩容
    6. for (int i = 11; i <=15 ; i++) {
    7. list.add(i);
    8. }

    源码分析:

    1. boolean add(E e);
    2. //执行add()方法进行添加时: (1)先确定是否需要扩容;(2)然后再执行赋值
    3. public boolean add(E e) {
    4. //ensureCapacityInternal()确定内部容量值
    5. ensureCapacityInternal(size + 1);
    6. elementData[size++] = e;
    7. return true;
    8. }
    9. private static final int DEFAULT_CAPACITY = 10;
    10. //int minCapacity首次添加数据时该值为1
    11. //int DEFAULT_CAPACITY = 10;
    12. private void ensureCapacityInternal(int minCapacity) {
    13. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    14. //Math.max(DEFAULT_CAPACITY, minCapacity)比较两者的值,然后取最大值
    15. //因此,调用无参构造器时,初始化容量为0,首次添加数据时容量变为10
    16. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    17. }
    18. //ensureExplicitCapacity()该方法确定最小容量值
    19. ensureExplicitCapacity(minCapacity);
    20. }
    21. private void ensureExplicitCapacity(int minCapacity) {
    22. modCount++;
    23. // overflow-conscious code
    24. //第一次进入时,int minCapacity = 10,elementData.length=0,调用grow()方法进行扩容
    25. //要想扩容必须满足minCapacity>elementData.length,才能调用grow()方法进行扩容
    26. //再此调用grow()时,minCapacity=size+1,elementData.length=10(每次扩容后会改变),
    27. if (minCapacity - elementData.length > 0)
    28. // grow()进行扩容
    29. grow(minCapacity);
    30. }
    31. private void grow(int minCapacity) {
    32. // overflow-conscious code
    33. // 存储旧的数组长度
    34. int oldCapacity = elementData.length;
    35. // 首次扩容时oldCapacity为0 因此: oldCapacity + (oldCapacity >> 1) = 0
    36. //第二次扩容变为原来的容量的1.5倍
    37. int newCapacity = oldCapacity + (oldCapacity >> 1);
    38. if (newCapacity - minCapacity < 0)
    39. //首次扩容将minCapacity=10赋值给新的容量newCapacity
    40. newCapacity = minCapacity;
    41. if (newCapacity - MAX_ARRAY_SIZE > 0)
    42. newCapacity = hugeCapacity(minCapacity);
    43. // minCapacity is usually close to size, so this is a win:
    44. //扩容后将原先的elementData的数据放到新的数组中
    45. elementData = Arrays.copyOf(elementData, newCapacity);
    46. }

    结论:使用无参构造器创建ArrayList对象,不会定义elementdata数组的长度,当第一次调用add(E e) 方法时,初始化定义底层数组的长度为10,之后调用add(E e)时,如果需需要再次扩容,则调用grow(int minCapacity) 进行扩容,长度为原来的1.5倍。

    2.初始化ArrayList有参构造器

     2.1 使用无参构造器创建ArrayList对象

         List list2 = new ArrayList(2);

      源码分析:调用有参构造器,创建了一个固定大小的elementData数组。

    1. //int initialCapacity = 2 初始化elementData数组大小为2
    2. public ArrayList(int initialCapacity) {
    3. //initialCapacity 是否大于0
    4. if (initialCapacity > 0) {
    5. //将值的大小赋值给elementData数组的大小
    6. this.elementData = new Object[initialCapacity];
    7. } else if (initialCapacity == 0) {
    8. //如果initialCapacity 为0,和创建无参构造方法流程一致
    9. this.elementData = EMPTY_ELEMENTDATA;
    10. } else {
    11. throw new IllegalArgumentException("Illegal Capacity: "+
    12. initialCapacity);
    13. }
    14. }

    源码分析:当数据数量超过elementData数组长度时,进行扩容

    1. List list2 = new ArrayList(2);
    2. for (int i=0;i<=1;i++){
    3. list2.add(i);
    4. }
    5. //进行数组扩容
    6. list2.add(3);
    7. boolean add(E e);
    8. public boolean add(E e) {
    9. //添加3时,需要ensureCapacityInternal()确定内部容量值
    10. //size+1=3
    11. ensureCapacityInternal(size + 1);
    12. elementData[size++] = e;
    13. return true;
    14. }
    15. private void ensureCapacityInternal(int minCapacity) {
    16. // elementData 数组不为{},不执行
    17. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    18. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    19. }
    20. //执行该方法,确定最小容量值
    21. ensureExplicitCapacity(minCapacity);
    22. }
    23. private void ensureExplicitCapacity(int minCapacity) {
    24. modCount++;
    25. //此时数据的大小minCapacity=3 大于elementData数组大小,进行扩容
    26. // overflow-conscious code
    27. if (minCapacity - elementData.length > 0)
    28. grow(minCapacity);
    29. }
    30. private void grow(int minCapacity) {
    31. // overflow-conscious code
    32. //oldCapacity = 2
    33. int oldCapacity = elementData.length;
    34. //newCapacity = 2*1.5
    35. int newCapacity = oldCapacity + (oldCapacity >> 1);
    36. if (newCapacity - minCapacity < 0)
    37. newCapacity = minCapacity;
    38. if (newCapacity - MAX_ARRAY_SIZE > 0)
    39. newCapacity = hugeCapacity(minCapacity);
    40. // minCapacity is usually close to size, so this is a win:
    41. //扩容后将原先的elementData的数据放到新的数组中
    42. elementData = Arrays.copyOf(elementData, newCapacity);
    43. }

    结论:调用有参构造器,创建了一个指定大小的elementData数组,如果需要再次扩容,则直接扩容elementData为原先的1.5倍。

    3.总结

            1)当创建方式为 List list = new ArrayList(0)时,默认调用EMPTY_ELEMENTDATA初始化容量为0,当首次添加元素时,elementData数组长度容量扩为 10,之后再次扩容时变为原先的1.5倍;

            2)当创建方式为 List list = new ArrayList(10)时,创建了一个指定长度的elementData数组,如果需要再次扩容时,则直接扩容elementData数组长度为原先的1.5倍。

            3)使用无参构造器创建ArrayList对象,不会定义elementdata数组的长度,当第一次调用add(E e) 方法时,初始化定义底层数组的长度为10,之后调用add(E e)时,如果需需要再次扩容,则调用grow(int minCapacity) 进行扩容,长度为原来的1.5倍。

            

  • 相关阅读:
    在idea中创建MyBatis核心配置文件和映射文件的模板、使用模板搭建MyBatis框架
    ASO优化如何做?3个核心要点必须掌握
    宽瞬时带宽放大器SKY66051-11、SKY66052-11、SKY66041-11、SKY66317-11(RF)适用于通讯网络
    Docker数据卷和网络管理 下
    大厂永恒敲门砖——Android 系统启动流程详解
    index.vue?3ae6:112 Uncaught TypeError: this.$message is not a function
    李宏毅《DLHLP》学习笔记7 - Voice Conversion 1
    职责链设计模式
    (Pytorch)简单了解torch.autograd.grad()以及torch.autograd.backward()
    Flask 学习-44.Flask-RESTX 请求参数校验reqparse.RequestParser()
  • 原文地址:https://blog.csdn.net/hzz_321/article/details/126879560