动态增长原理:
首先看一下往ArrayList加对象的方法add&addAll():
- add():
/**
* Appends the specified element to the end of this list.
添加到list的最后面
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
- addAll():
/**
添加到最后面
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length; //确定增加的对象的长度
ensureCapacityInternal(size + numNew); // Increments modCount 当前list和增加对象的list,进行动态扩容
System.arraycopy(a, 0, elementData, size, numNew);
//复制
size += numNew;
return numNew != 0;
}
/**
在索引位置插入
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount 先扩容
//开始移动
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
从上述方法可以看出,add之后,如果是单个元素,size+1 入参到 ensureCapacityInternal()中,如果是一个集合类型,会将其转化为Object[]确认其长度,再size+numNew入参到ensureCapacityInternal()中。
- ensureCapacityInternal()方法:
//确认内部容积
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
调用了2个方法,ensureExplicitCapacity(int),入参为calculateCapacity(elementData, minCapacity)
- calculateCapacity(elementData, minCapacity)方法:这是一个静态方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
计算容积,如果是空的(第一次),则返回默认容积10 和最小容积的最大值,其实就是10或者更大的,
如果不是第一次添加,那直接就返回最后要加的值。
- ensureExplicitCapacity(int minCapacity)方法:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
可以知道这是确认清楚容量,如果计算的min容量和目前所装的长度差大于0,则说明需要扩容。
- 增长:提高容量以便至少满足最少 minCapacity 容量!!
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*通过min容量参数,增大容量以确保他能至少装下元素
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //原来的长度
int newCapacity = oldCapacity + (oldCapacity >> 1); //新长度 扩容1.5倍
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);
//最后复制,新数组,结束
}
这个方法就是在增大容量,扩1.5倍如果不够就直接变到指定的容量,然后判断是否超过了数组最大容量,超过就调用调用hugeCapacity方法。
这个方法首先计算出一个容量,大小为oldCapacity + (oldCapacity >> 1)。即elementData数组长度的1.5倍。再从minCapacity 和
这个容量中取较大的值作为扩容后的新的数组的大小。
- MAX_ARRAY_SIZE参数:
/**
* 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即有些虚拟机会在数组中保存 header words 头部字。
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //防止内存溢出
即有些虚拟机会在数组中保存 header words 头部字。
- 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;
}
因为数组理论上长度就是 Integer.MAX_VALUE 个别JVM 设计上的问题 咱们可以尽量照顾下 但并不是说一定因为个别JVM 就一定不让扩容到 整数最大值长度。
如果再满了 那么对不起 直接到将数组长度设置为整数最大值, 爱咋咋地!
要分配的数组的最大大小(除非必要)。 有些虚拟机在数组中保留一些头词。 尝试分配更大的数组可能导致 OutOfMemoryError:请求的数组大小超过虚拟机限制
因此,数组最大容量是 Integer.MAX_VALUE . ,在图示情况扩容到 MAX_ARRAY_SIZE 是为了扩容到 MAX_ARRAY_SIZE以上长度就OOM的虚拟机可以尽量不OOM,如果还放不下没办法,对不起了大兄弟!
通过分析可以发现,ArrayList的扩容会产生一个新的数组,将原来数组的值复制到新的数组中。会消耗一定的资源。所以我们初始化ArrayList时,最好可以估算一个初始的大小。
https://stackoverflow.com/questions/35756277/why-the-maximum-array-size-of-arraylist-is-integer-max-value-8