zoukankan      html  css  js  c++  java
  • 关于ArrayList的扩容机制

    关于ArrayList的扩容机制


            ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效率较高,相对同是数组实现的Vector,并不能保证线程安全,所以多适用于单线程环境。

    由于底层是有数组实现,因为数组的长度需要初始化定义,并不能自动进行长度增加,所以ArrayList有对应的扩容机制,当增加元素时,会判断是否需要扩容,下面看源码:

    首先认识ArrayList的几个重要变量:

        /*序列化ID*/
        private static final long serialVersionUID = 8683452581122892189L;
        /*默认的数组容量,第一次进行添加操作时,如果ArrayList为空或者添加元素后的数组长度小于等于10,会将该值作为数组的初始长度*/
        private static final int DEFAULT_CAPACITY = 10;
        /*一个空的对象数组,在需要将elementData数组置空时,会将该对象赋予elementData*/
        private static final Object[] EMPTY_ELEMENTDATA = {};
        /*默认的空对象数组,在ArrayList构造时,默认将该对象给elementData*/
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        /*ArrayList内部用于存储元素的数组,transient关键字修饰,不使用默认序列化*/
        transient Object[] elementData; // non-private to simplify nested class access
        /*数组的长度,是ArrayList中实际元素的个数,不是数组的容量*/
        private int size;
    在ArrayList的尾部插入和其他位置虽然是不同方法,但是都使用到了ensureCapacityInternal()方法确保数组内部容量,在每次添加操作中都会使用该方法进行容量判断,之后,才会将增加的元素添加到数组中,下面以尾部插入为例;
       /**
         * 增加数据元素到集合得末尾
         * 
         */
        public boolean add(E e) {
            /*判断是否扩容,如果原来的元素个数是size,那么增加一个元素之后的元素个数为size + 1,所以需要的最小容量就为size + 1*/
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }

     ensureCapacityInternal()将判断委托给ensureExplicitCapacity()处理

        /*获取数组最小容量*/
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            /*如果elementData为空,且minCapacity <= 10,都会以DEFAULT_CAPACITY作为最小容量*/
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
    
        private void ensureCapacityInternal(int minCapacity) {
            /*ensureCapacityInternal方法委托给ensureExplicitCapacity方法*/
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            /*如果minCapacity大于elementData的长度,使用grow方法进行扩容*/
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }

     下面就是扩容的实现方法grow方法:

        /*扩容方法*/
        private void grow(int minCapacity) {
            /*原有数组容量*/
            int oldCapacity = elementData.length;
            /*新的数组容量,下面位运算相当于newCapacity = oldCapacity * 1.5 向下取整*/
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            /*如果新的数组容量小于需要的最小容量,即假设新的数组容量是15,最小需要16的容量,则会将16赋予newCapacity*/
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            /*变量MAX_ARRAY_SIZE = 2147483639 [0x7ffffff7],如果扩容后的新容量大于这个值则会使用hugeCapacity方法
             * 判断最小容量minCapacity是否大于MAX_ARRAY_SIZE,如果需要最小容量的也大于MAX_ARRAY_SIZE,则会以
             * Integer.MAX_VALUE = 2147483647 [0x7fffffff]的值最为数组的最大容量,如果没有则会以MAX_ARRAY_SIZE最为最大容量
    * MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,为什么用MAX_ARRAY_SIZE ,源码的中的说法是一些虚拟机中会对数组保留一些标题字段
    * 使用Integer.MAX_VALUE会造成内存溢出错误 *
    */ if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); /*确定数组最终的容量newCapacity之后,将原有ArrayList的元素全部拷贝到一个新的ArrayList中*/ elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { /*如果minCapacity小于0,则抛出内存溢出错误*/ if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

     ArrayList的扩容机制还是相对容易理解的,就是在第一个添加元素时,创建一个长度为10的数组,之后随着元素的增加,以1.5倍原数组的长度创建一个新数组,即10, 15, 22, 33,。。这样序列建立,将原来的元素拷贝到新数组之中,如果数组长度达到上限,则会以

    MAX_ARRAY_SIZE 或者 Integer.MAX_VALUE作为最大长度,而多余的元素就会被舍弃掉。
  • 相关阅读:
    CentOS yum 源的配置与使用
    在css当中使用opacity
    CSS position属性absolute relative等五个值的解释
    uni APP 微信小程序获取授权的微信信息
    vue-admin-element 打包发布后IE报错的问题
    RMAN恢复数据文件
    怎么删除表空间对应的某一个数据文件
    Oracle存储过程、函数、包加密wrap
    Oracle加密解密
    Interval 用法总结
  • 原文地址:https://www.cnblogs.com/zhexuejun/p/11149406.html
Copyright © 2011-2022 走看看