ArrayList扩容

2019-07-10  本文已影响0人  ZhiJunPan
众所周知,ArrayList是基于数组实现的,而在使用的时候我们从未担心过它的容量问题,毫无疑问这部分工作在源码中进行维护以及实现了。

这篇文章做一个研究源码的记录,使用的jdk版本是jdk11(不同的jdk版本源码可能不同)
首先查看ArrayList的构造函数,有三种方式来初始化ArrayList,分别是指定容量,不指定容量,以及用集合来初始化。

public ArrayList(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);
        }
    }

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) { 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

使用默认构造方法的时候,先初始化一个空的数组,而在第一次插入的时候,将其初始化为默认容量的数组,默认容量为10.

只有在add或者addAll的时候会触发扩容机制。
首先我们先来考虑第一次插入元素的情况,一下为add的源码:

 private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

第一次插入的时候,size是0,数组长度也为0,这是需要执行grow方法,接下来我们进入grow方法:

  private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

    private Object[] grow() {
        return grow(size + 1);
    }

可以看到grow将size+1作为最小需要内容传入重载的grow(int minCapacity)方法,这个方法返回数组拷贝,拷贝大小为newCapacity方法返回的内容。接下来进入newCapacity方法:

  private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

可以看到新容量是旧容量的1.5倍,如果扩容容量小于需要的容量,满足if条件,对比数组是否为空数组,如果是空的话,数组容量为默认容量和最小需要容量的更大值(防止空间不足,第一次插入情况),若不为空,直接返回所需要容量。不满足if条件的话,如果返回新容量大于最大数组容量,则使用hugeCapacity方法确认容量,超过数组容量最大值的话返回int最大值,否则返回数组容量最大值。单个非首次add的情况不会触发新容量小于最小需要容量的情况。hugeCapacity方法:

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }

而使用addAll方法的时候,则可能触发该条件。
使用情况:

当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 hugeCapacity 方法。数组容量为10,add方法中 return true,size增为1。
当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11

上一篇下一篇

猜你喜欢

热点阅读