一文讲清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。
希望你读完本文有所收获!!