ArrayList 的扩容机制
前言
ArrayList 底层是基于数组实现的,不过得益于其扩容机制,它可以看作是一个动态的数组,弥补了数组长度是定长的缺陷。
在往 ArrayList 里添加元素时,如果添加完元素之后,元素的总个数大于当前数组的容量,会执行扩容,这个扩容的过程可以归纳为以下两个步骤:
- 确定新数组的容量
- 将老数组内的元素拷贝到新数组中去
确定新数组的容量这一步是本文重点,接下来我们一起来看一下。
确定扩容后的数组容量
阅读 ArrayList 的源码,我们会发现不论是执行 add() 还是 addAll() 方法(包括 add() 、addAll() 方法的重载版本也是一样,只不过下面没有贴出来),它在正式添加元素之前,都会先执行
ensureCapacityInternal(int minCapacity)这个方法来确保容量足够。
传入的参数是添加完元素之后元素的总个数(但要清楚此时还没有进行元素的添加),即扩容后的最小容量:minCapacity,我希望你暂时记住这个参数的意义,后面的源码会不停的根据这个参数做文章。
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
我们接下来去到 ensureCapacityInternal() 方法里面去看一看。
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
在
ensureCapacityInternal(int minCapacity)方法里面,它首先会调用
calculateCapacity(Object[] elementData, int minCapacity)这个方法。
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
calculateCapacity() 这个方法会做一个简单的判断,如果当前的数组是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认容量的数组,这个数组是通过 ArrayList 默认的构造方法创建出来的),会返回默认容量与 minCapacity 中的最大值。ArrayList 的默认容量是 10.
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
之后它会调用
ensureExplicitCapacity(int minCapacity).
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
这个方法也是做了一个简单的判断,它会检查 minCapacity 是否大于当前数组的大小,如果大于,那么就会调用
grow(int minCapacity)这个方法执行扩容。
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(int minCapacity)这个方法除了最后一行是将原数组的数据拷贝到新数组中去,其它所有的代码都是在确定新数组的容量。
我们从头到尾来梳理一下。
.
int newCapacity = oldCapacity + (oldCapacity >> 1);它首先会将容量变为原来的 1.5 倍,然后将这个 newCapacity 与 minCapacity 进行对比。如果 newCapacity 还是比 minCapacity 小,就将 newCapacity 置为 minCapacity。
之后将 newCapacity 与 ArrayList 的数组大小阈值进行对比,这个阈值是 Integer.MAX_VALUE - 8,即 2^31 - 1 - 8.
/** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
如果 newCapacity 大于这个阈值,那么就调用
hugeCapacity(int minCapacity)这个方法,这里请睁大眼睛,它传入的参数是 minCapacity,而不是 newCapacity!
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
之后,这个方法第一步做的这个判断,让我百思不得其解,因为就目前 ArrayList 这个实现方式来看,minCapacity 是无论如何都不可能出现小于 0 的情况,所以这里大家可以忽略这段代码。
< 1c6f4 p>最后我们来看一下返回值,这个是 hugeCapacity() 方法的重点。它会判断 minCapacity与 MAX_ARRAY_SIZE 这个阈值的大小,如果 minCapacity 大的话,就直接将 newCapactiy 定为 Integer.MAX_VALUE,否则使用 MAX_ARRAY_SIZE 这个阈值作为新数组的容量,即 newCapactiy 为 Integer.MAX_VALUE - 8.- ArrayList集合底层源代码展示以及结构解析,扩容机制
- ArrayList扩容机制
- ArrayList的扩容机制
- ArrayList源码分析(fail-fast机制和扩容)
- ArrayList动态扩容机制
- 浅谈java1.8ArrayList源码(扩容机制,快速失败机制)
- [Java集合源码阅读]-ArrayList扩容机制
- ArrayList,HashMap,LinkedList 初始化大小和 扩容机制
- 透过源码角度分析ArrayList扩容机制
- ArrayList和Vector的扩容机制
- ArrayList、HashMap、LinkedList、Vector 初始化大小和 扩容机制
- JDK1.7——ArrayList扩容机制
- ArrayList和Vector的扩容机制
- ArrayList和Vector的扩容机制
- ArrayList扩容机制
- ArrayList的扩容机制
- 关于ArrayList扩容机制
- 对Java ArrayList的自动扩容机制示例讲解
- Java ArrayList的自动扩容机制
- ArrayList动态扩容机制--源码解析