美文网首页
ArrayList扩容

ArrayList扩容

作者: 鸡杂面 | 来源:发表于2020-05-31 10:33 被阅读0次

参考:
https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/ArrayList-Grow.md

一、初始化

0、内部组成
ArrayList内部有一个数组elementData来存放数据,还有一个整数size来记录实际的元素个数。

private transient Object[] elementData;
private int size;

重要的数据:

  1. DEFAULT_CAPACITY 默认容器大小
  2. DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认空数组,调用空参构造函数时elementData的默认值
/**
     * 默认初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;
    

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

1、以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。
源码:

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

2、使用带初始容量参数的构造函数时(用户自己指定容量),在参数合法的情况下(大于等于0),分配给指定大小的数组。
源码:

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {//初始容量大于0
            //创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {//初始容量等于0
            //创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {//初始容量小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

二、扩容机制

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

当我们调用add方法时,add方法会先调用ensureCapacityInternal(size + 1)

2. ensureCapacityInternal方法(容量最小值为10)

add方法首先调用了ensureCapacityInternal(size + 1)

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

        ensureExplicitCapacity(minCapacity);
    }

当ArrayList是无参构造且第一次调用add时,minCapacity 会被赋予DEFAULT_CAPACITY的值也就是10;

3. ensureExplicitCapacity(判断是否需要扩容)

ensureCapacityInternal一定会调用ensureExplicitCapacity(minCapacity);

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

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

最少所需容量minCapacity 大于内置数组elementData的长度时,调用grow(minCapacity)扩容

4. grow(1.5倍扩容)扩容核心

ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity为偶数就是1.5倍,否则是1.5倍左右)! 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数.

/**
     * 要分配的最大数组大小
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * ArrayList扩容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity为旧容量,newCapacity为新容量
        int oldCapacity = elementData.length;
        //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
        //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
       // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
       //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
4. hugeCapacity

从上面 grow() 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) hugeCapacity() 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为Integer.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8。

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;
    }

ArrayList扩容机制总结

ArrayList的底层是数组,有两个核心内置元素:
1、 elementData 是其底层数组,存放数据元素的地方。
2、 size是当前存放元素的个数。
初始化时,若使用无参构造,elementData 指向一个空数组;若使用带参构造,则elementData 数组的初始容量大小和参数相同。
使用add方法
1、无参构造并且未进行添加操作的elementData 数组容量变为10;
2、其它情况下,elementData 数组应该有的最小容量为minCapacity =size+1。如果minCapacity 大于Integer.MAX_VALUE,抛出溢出异常;如果minCapacity 小于等于elementData 当前容量,不用进行扩容操作;如果minCapacity 大于elementData 当前容量,elementData 的容量扩容为原来的1.5倍。如果扩容后的容量不大于minCapacity ,则elementData 的容量扩容为minCapacity ;如果如果扩容后的容量大于MAX_ARRAY_SIZE,当minCapacity > MAX_ARRAY_SIZE时elementData 的容量扩容为Integer.MAX_VALUE,当minCapacity <MAX_ARRAY_SIZE时elementData 的容量扩容为MAX_ARRAY_SIZE。

相关文章

  • 讲讲ArrayList的扩容和什么是溢出感知代码

    ArrayList 的扩容分为主动扩容和自动扩容两种。主动扩容就是通过调用 ArrayList 提供的 ensur...

  • Java动态数组实现(模拟ArrayList)

    目标:手动实现一个动态数组,模拟ArrayList ArrayList会自动扩容,先来看一下ArrayList扩容...

  • ArrayList 扩容 Android Java 真的不一

    以前学java基础的时候 看过ArrayList的扩容机制 实现原理是下面这样 当时做的笔记ArrayList扩容...

  • ArrayList源码分析

    问题提出 ArrayList底层采用什么数据结构? ArrayList是如何扩容的? 频繁扩容导致性能下降如何处理...

  • ArrayList动态扩容机制

    以ArrayList中add方法,讲解ArrayList动态扩容机制 扩容判断每次增加元素时,用size+1计算出...

  • ArrayList动态扩容机制--源码解析

    ArrayList动态扩容机制--源码解析 阅读原文 首先我们通过一个具体的例子看一下ArrayList的扩容效果...

  • ArrayList扩容

    重要属性 初始扩容 如果容量从0到1,比如调用add(E e),会将数组elementData变成{obj,nul...

  • ArrayList扩容

    众所周知,ArrayList是基于数组实现的,而在使用的时候我们从未担心过它的容量问题,毫无疑问这部分工作在源码中...

  • ArrayList扩容

    参考:https://github.com/Snailclimb/JavaGuide/blob/master/do...

  • java集合类-2-List

    ArrayList 属性 普通方法 添加元素 扩容 扩容系数:1.5 查找 删除 不调整elementData,t...

网友评论

      本文标题:ArrayList扩容

      本文链接:https://www.haomeiwen.com/subject/avqzmhtx.html