• Java ArrayList扩容底层原理深挖


    今儿咱来看看ArrayList是怎么扩容的,底层是什么样的

    先说结论

    1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
    2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
    3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
    4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。

    首先咱先看看空参构造

    1. ArrayList al = new ArrayList<>();
    2. //底层源码
    3. public ArrayList() {
    4. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    5. }
    6. Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    7. transient Object[] elementData;

    elementData是集合中存储对象的数组

    DEFAULTCAPACITY_EMPTY_ELEMENTDATA这玩意是一个对象的空数组

    然后往里面添加元素

    1. al.add(5);
    2. //底层源码
    3. public boolean add(E e) {
    4. ensureCapacityInternal(size + 1); // Increments modCount!!
    5. elementData[size++] = e;
    6. return true;
    7. }
    8. private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
    9. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    10. }
    11. private static int calculateCapacity(Object[] elementData, int minCapacity) {
    12. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    13. return Math.max(DEFAULT_CAPACITY, minCapacity);
    14. }
    15. return minCapacity;
    16. }
    17. private void ensureExplicitCapacity(int minCapacity) {
    18. modCount++;
    19. // overflow-conscious code
    20. if (minCapacity - elementData.length > 0)
    21. grow(minCapacity);
    22. }
    23. private void grow(int minCapacity) {
    24. // overflow-conscious code
    25. int oldCapacity = elementData.length;
    26. int newCapacity = oldCapacity + (oldCapacity >> 1);
    27. if (newCapacity - minCapacity < 0)
    28. newCapacity = minCapacity;
    29. if (newCapacity - MAX_ARRAY_SIZE > 0)
    30. newCapacity = hugeCapacity(minCapacity);
    31. // minCapacity is usually close to size, so this is a win:
    32. elementData = Arrays.copyOf(elementData, newCapacity);
    33. }
    34. public static T[] copyOf(T[] original, int newLength) {
    35. return (T[]) copyOf(original, newLength, original.getClass());
    36. }

    这是全部的底层代码,我们分开一点一点看

     

     

    1. public boolean add(E e) {
    2. ensureCapacityInternal(size + 1); // Increments modCount!!
    3. elementData[size++] = e;
    4. return true;
    5. }

    size是记录集合中元素个数的成员变量

    调用了ensureCapacityInternal方法,传入size+1,

    让下一个数组元素等于添加的对象,返回添加成功

     

    1. private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
    2. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    3. }

    calculateCapacity:用在计算数组的容量,它的参数是一个对象数组和一个最小需要的容量。

    ensureExplicitCapacity:用在数组容量不足时扩充数组的大小,它的参数是最小需要的容量。

     

    1. private static int calculateCapacity(Object[] elementData, int minCapacity) {
    2. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    3. return Math.max(DEFAULT_CAPACITY, minCapacity);
    4. }
    5. return minCapacity;
    6. }
    7. private static final int DEFAULT_CAPACITY = 10;

    DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个常量数组是在ArrayList类中定义的,用于表示一个空的ArrayList。

    如果是空的,就返回默认容量(就是10)和最小需要的容量中较大的一个,即Math.max(DEFAULT_CAPACITY, minCapacity)。

    人话就是如果集合为空,那就看看所需最小容量和默认容量哪个大,如果默认大就返回10,开辟一个长度为10的数组,如果所需最小容量大,就返回所需最小容量,开辟一个长度为所需最小容量的数组。

    那为什么会出现所需最小容量大于默认容量呢?

    答案是因为有一个addAll方法可以批量添加数据。

     

     

    1. private void ensureExplicitCapacity(int minCapacity) {
    2. modCount++;
    3. // overflow-conscious code
    4. if (minCapacity - elementData.length > 0)
    5. grow(minCapacity);
    6. }

    modCount++,如果所需最小长度大于这个它本身的长度,那就进行扩容。

     

     

     

    1. private void grow(int minCapacity) {
    2. // overflow-conscious code
    3. int oldCapacity = elementData.length;
    4. int newCapacity = oldCapacity + (oldCapacity >> 1);
    5. if (newCapacity - minCapacity < 0)
    6. newCapacity = minCapacity;
    7. if (newCapacity - MAX_ARRAY_SIZE > 0)
    8. newCapacity = hugeCapacity(minCapacity);
    9. // minCapacity is usually close to size, so this is a win:
    10. elementData = Arrays.copyOf(elementData, newCapacity);
    11. }

    首先计算新的容量,为旧的容量加上一半,即oldCapacity + (oldCapacity >> 1)。这里使用了右移运算符>>,相当于除以2。


    然后判断新的容量是否大于等于最小需要的容量,如果不是,就把新的容量设为最小需要的容量。
    接着判断新的容量是否超过了数组的最大限制,即MAX_ARRAY_SIZE。如果是,就调用一个叫hugeCapacity的方法,来返回一个合适的容量。


    最后,使用Arrays.copyOf方法,把原来的数组复制到一个新的数组中,并把新的数组赋值给elementData。

     

    总结一下吧,ArrayList扩容底层大概分为四步:

    1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
    2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
    3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
    4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。

     

     

     

  • 相关阅读:
    spring框架有哪些优点呢?
    mysql聚簇索引和主键索引的区别
    mysql增加分区
    Java面向对象—继承
    微擎模块 志汇超级外卖餐饮小程序 V5.8.0后台模块+前端小程序
    单向的2.4G频段RF射频芯片-SI24R2E
    Java错题归纳day11
    《入门docker,这一篇就够了》
    python爬虫实战:获取电子邮件和联系人信息
    uniCloud开发微信公众号:四、引入/封装redis缓存方法
  • 原文地址:https://blog.csdn.net/weixin_73922932/article/details/130899867