zoukankan      html  css  js  c++  java
  • ArrayList底层扩容机制

    先来看下没有指定参数的List集合:

    // 使用无参构造创建List集合
    List list = new ArrayList();
    // 添加数据
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
    // 添加数据
    for (int i = 11; i <= 15; i++) {
        list.add(i);
    }

    话不多说直接上源码:

    1、初始化集合

    /**
     * 初始化一个容量为0的列表
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    

    2、添加数据:涉及一个自动装箱

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    

    3、执行list.add方法:

    1.这里进行判断容量是否够不够。

    2.然后再进行赋值操作

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    3.执行ensureCapacityInternal方法

      我们在for循环中添加的i就是minCapacity(1)跟DEFAULT_CAPACITY(常量为10),,进行比较,发现DEFAULT_CAPACITY大,就将这个10赋值给list集合无参构造器。也就是初始化容量为10

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    4、调用grow完成底层的扩容

    modCount++:记录集合被修改的次数,防止有其他线程进来修改

    minCapacity(10)-elementData.length(0) > 0;  实际上需要的最小容量 - Object数组初始化容量 > 0,elementData大小不够,才会调用底层的grow方法执行扩容。

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

    5、grow扩容方法

    第一次进来:

      elementData.length为0,oldCapacity + (oldCapacity >> 1)是等于0的;           那么第4行代码不会执行,也就是第一次不会执行扩容

    第5行的if执行:

      if (newCapacity - minCapacity < 0)   第一次进来这个if语句会被执行,0 - 10 是小于0的,那么就将minCapacity(10)赋值给newCapacity,即newCapacity为10.

    第7行的if不会执行:

      因为10-int类型最大值,不大于0

    第10行的数组拷贝执行:

      就是说将elementData(0),扩容为newCapacity(10)大小,没有数据,那么就是数组中有10个空数据     

    这里使用Arrays.copyOf()就是为了保留原有数组的数据的情况下,然后增加新的数组(null)

    现在elementData为10个大小了,第二次及其以后都是按照1.5(oldCapacity + (oldCapacity >> 1)进行扩容了。)

     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 }

    6、放入第一个数据

    回到第二步的:elementData[size++] = e;

      elementData数组为10,这时候size默认是为0的,就将数据放到数组的第一个位置上面:elementData[0] = 1;

    这里注意的是,第一次扩容完毕数组大小为10,只有这10个数组容量没有了才会继续执行扩容,就说明在第一次扩容后可以添加for循环中1-10这十个数据~

     7、第二次扩容:数组容量不够存放1-10个数据,执行扩容

    执行到11-15数据的添加位置上:继续回到第五步的grow继续执行扩容

     

    扩容后的elementData数组:

    end:第三次扩容不显示库容后的容量问题

    很多小伙伴在debug的时候,发现1-10正常显示,11-15正常显示(上图),但是执行到第三次扩容的时候(10+15/2 = 22)时候并没有看到null。

    这是因为idea的原因,只需要在设置中调试一下:

    对比一下没设置跟设置后的:

  • 相关阅读:
    区块链|肖臻《区块链技术与应用》公开课之以太坊篇
    区块链|肖臻《区块链技术与应用》公开课之比特币篇
    复合数据类型
    广播变量和累加器
    mysql_基本操作
    8.10 NOI模拟赛 fzhtql SAM 后缀数组 启发式合并 dsu on tree 树状数组 set 线段树合并
    心态
    7.29 NOI 模拟赛 Stars 状压 dp
    8.2 NOI模拟赛 Palindrome dp 组合计数 分类讨论
    7.30 NOI模拟赛 B Easy Sum 分块 NTT
  • 原文地址:https://www.cnblogs.com/zhangzhixi/p/14891730.html
Copyright © 2011-2022 走看看