一文讲清ArrayList扩容机制

对于ArrayList大家一定不会陌生,它就是传说中的动态数组。ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长,使用起来十分便利。

它继承于 AbstractList,实现了List、RandomAccess、Cloneable这些接口。 对于List和Cloneable大家一定都很熟悉,RandomAccess就不一定了。其实RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在ArrayList中我们可以通过get()快速访问某个元素。

介绍完了ArrayList接下来我们进入正题,谈一谈ArrayList的扩容机制。、

ArrayList的构造函数

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


 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(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

 

我们可以看到,上面是ArrayList的三个构造函数,如果说我们不会传递参数进去,那么就会默认的去调用无参的构造函数,那么ArrayList的容量就是默认大小也就是10。
如果我们传递一个参数进入ArrayList,那么ArrayList的默认大小就是这个参数的值。

ArrayList的add()方法

既然我们是讨论扩容机制,那么什么情况下才会引发扩容?没错,就是你向ArrayList中添加一个元素的时候,它才会触发扩容机制。我们看一下add()的源码。

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

我们可以看到,在把e赋值给数组之前,add方法调用了ensureCapacityInternal()方法,来判断是否需要扩容。那么接下来我们就沿着ensureCapacityInternal()方法的源代码,一步步去了解扩容的原理。

 

ArrayList的扩容机制

 

ensureCapacityInternal()方法

 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal方法接受了size+1作为minCapacity,并且进行判断如果数组是空数组,那么10和minCapacity的较大值就作为新的minCapacity。接下来会传递minCapacity到ensureExplicitCapacity()方法中做进一步的判断。

 

ensureExplicitCapacity()方法

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

接下来会判断我们传入的minCapacity 和 elementData.length的大小,如果 elementData.length大于minCapacity,说明数组容量够用,就不需要扩容。反之,则传入minCapacity到grow()方法中,开始扩容。

grow()方法

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

 

进入grow方法,我们会将newCapacity设置为旧容量的1.5倍,这也是ArrayList每次扩容都为原先容量1.5倍的由来。然后会进行判断,如果newCapacity小于minCapacity,那么就将minCapacity的值赋予newCapacity

然后再检查新容量是否超出了定义的最大容量,如果超出则调用hugeCapacity方法,比较minCapacity和MAX_ARRAY_SIZE的值。如果minCapacity大,那么新容量为Integer.MAX_VALUE,否则新容量为MAX_ARRAYSIZ。最后调用Arrays.CopyOf传递elementData和新容量,返回新的elementData。

 

希望你读完本文有所收获!!

 

posted on 2019-08-05 17:23  将图南  阅读(2049)  评论(0编辑  收藏  举报