ArrayList源码分析(容量与初始化)

当ArrayList 使用无参构造时,默认大小为10 ,也就是说在第一次add 的时候,分配为10 的容量,后续的每次扩容都会调用Array. copyOf 方法,创建新数组再复制。可以想象, 假如需要将1000个元素放置在ArrayList 中,采用默认构造方法,则需要被动扩容13 次才可以完成存储。反之,如果在初始化时便指定了容量new ArrayList(l 000), 那么在初始化ArrayLi st 对象的时候就直接分配1000 个存储空间,从而避免被动扩容和数组复制的额外开销。最后,进一步设想,如果这个值达到更大量级, 却没有注意初始的容量分配问题, 那么无形中造成的性能损耗是非常大的, 甚至导致OOM 的风险。所以,一定不要使用无参的方式构建ArrayList,构建时要设置合理的容量。

主要成员变量有两个:Object[] elementData和int size。一个用于存放数组元素,一个代表现有的元素个数。elementData的长度,就是当前的容量,默认为10,已oldCapacity + (oldCapacity >> 1)的方式进行扩容。最大容量为Integer.MAX_VALUE

elementData就是元素存放的数组。在构建的时候,如果提供了Capacity,直接就对elementData初始化了,如果是无参构造,则为定义好的长度为0的数组。无参构造在第一次添加元素时,会构建一个长度为10的对象数组。

/**
     *默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 用于默认大小的空实例的空数组实例,分开是为了第一次添加元素时扩容
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储ArrayList元素的数组缓冲区,
     * 第一次添加元素时会被扩充到10(DEFAULT_CAPACITY)
     * 就是Arraylist真正存储元素的地方。
     * 注意这里是Object。ArrayList中的元素都被强转为Object存储在这地方了
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 表示元素的个数
     */
    private int size;

    /**
     * 构造具有初始化容量的数组
     * 构建了元素数组
     */
    public ArrayListInit(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        }
    }

    /**
     * 无参构造方法,第一次添加元素时初始化为10容量
     */
    public ArrayListInit() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 把c转换为数组对象,赋值给elementData
     */
    public ArrayListInit(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {//如果有元素
            if (elementData.getClass() != Object[].class)//强转为Object.
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {//如果没有元素
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    /**
     * 对ArrayList进行瘦身,有多少个元素,容量就变为多少
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
                    ? EMPTY_ELEMENTDATA
                    : Arrays.copyOf(elementData, size);
        }
    }

    /**
     * 设置所需的最小容量
     * 如果进行一次扩容后的容量小于minCapacity,
     * 则把容量变成minCapacity,否则就是扩容后的容量
     * @param   minCapacity   the desired minimum capacity
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                ? 0 : DEFAULT_CAPACITY;
        if (minCapacity > minExpand) {//需要进行容量最小确认
            ensureExplicitCapacity(minCapacity);
        }
    }

    //确保容量不小于minCapacity,如果小于就扩容
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//变动次数
        // overflow-conscious code//扩充判断
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * list最大容量Integer.MAX_VALUE
     * OutOfMemoryError: 超过Integer.MAX_VALUE才会内存溢出(实际上上超出就是负数了)
     * hugeCapacity,最大的容量是Integer.MAX_VALUE
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 增加容量newCapacity = oldCapacity + (oldCapacity >> 1)
     * 扩容为minCapacity或oldCapacity + (oldCapacity >> 1),谁大就是谁
     * add元素时minCapacity=size+1;
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //可能会溢出
        //正数带符号右移的值肯定是正值,所以oldCapacity+(oldCapacity>> 1)
        //的结果可能超过int 可以表示的最大值,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果newCapacity<minCapacity则newCapacity = minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//size+1
        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);
    }

    //溢出检测,最大为Integer.MAX_VALUE,超出其实就是负数了就oom了
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

    /**
     *先确保容量不小于size + 1,再添加元素,直接通过数组下标赋值
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     * 移除指定位置的元素
     * @return 返回被移除的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);//溢出检测
        modCount++;//变动次数
        E oldValue = elementData(index);//返回时进行了强转

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值