ArrayList 初始容量
ArrayList
有多个不同的构造方法,不同的构造方法的初始容量是不同的。介绍之前先看下 ArrayList
都有哪些变量
// 默认初始化容量=10
private static final int DEFAULT_CAPACITY = 10;
// 空数组,当初始化容量为0时返回该数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空数组,用于跟 EMPTY_ELEMENTDATA 区分开来,当使用默认构造方法创建的时候返回该数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素存放的数组
transient Object[] elementData;
// 元素个数(即list长度)
private int size;
// 记录数组被修改的次数
protected transient int modCount = 0;
// 数组最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
- 无参构造方法,使用默认容量(默认容量为 10),并且设置
elementData
=DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 传入初始容量(
initialCapacity
),判断传入容量的值。值 > 0,则 new 一个长度为initialCapacity
的 Object 数组;值 < 0,直接设置elementData
=EMPTY_ELEMENTDATA
- 传入一个
Collection
,如果Collection
的长度为 0,同样的,设置elementData
=EMPTY_ELEMENTDATA
;否则,调用Collection
的toArray
方法创建一个数组,并复制给elementData
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
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
有两个概念,一个是 capacity
,它表示“容量”
,表示的是 elementData 的长度。另一个是 size
,表示的是数组中"存放元素的个数
,这个很好理解,我们经常调用 ArrayList
的 size()
方法返回的就是这个 size
ArrayList 如何实现扩容的?
以无参构造方法为例,初始容量为 10,当调用 add(E e)
方法往 ArrayList
中插入数据的时候,才真正分配容量,即调用 Add(E e)
方法添加第一个元素时,数组扩容为 10
以 add(E e)
方法为入口,分析源码
/**
* Appends the specified element to the end of this 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;
}
每次调用 add(E e)
方法,都会调用 ensureCapacityInternal
方法,调用完 ensureCapacityInternal
方法后,将元素添加到 elementData
数组的尾部,进入到 ensureCapacityInternal
方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
add 第一个元素的时候,minCapacity
为 1 ,因为是添加第一个元素,此时 elementData
== DEFAULTCAPACITY_EMPTY_ELEMENTDATA
结果为true
,通过 Math.max
方法计算出 minCapacity
为 10
接下来看 ensureExplicitCapacity
方法,方法判断 minCapacity
是否大于数组长度,大于的话,表示数组需要扩容,不扩容的话直接往数组添加元素会导致数组越界异常,掉调用 grow()
方法进行数组的扩容
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
默认情况下,新建容量(newCapacity
)会是原来容量(oldCapacity
)的 1.5 倍(这里使用了 >> 位运算符提高效率),一般情况下,如果扩容 1.5 倍后就大于最小容量(minCapacity
),则使用这个 1.5 倍的容量,如果小于最小容量,就返回最小容量的值
当新建容量(newCapacity
)大于最大数组长度(MAX_ARRAY_SIZE
)的时候,调用 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;
}
该方法很简单,当 minCapacity
< 0 的时候,抛出内存溢出异常,否则返回最大数组长度
最后,调用 Arrays.copyOf
方法新建一个原数组的拷贝,并修改原数组指向新创建的数组。原数组会被垃圾回收器回收
扩容总结
一般情况下,ArrayList
在调用 add
方法的时候,如果 size
长度不够,则会进行扩容操作,将数组长度扩容到原来的 1.5 倍。如果频繁对 ArrayList
新增元素,则会频繁执行扩容操作影响性能
为避免频繁扩容造成性能下降。当知道将要存入数组的元素数量的时候,可以调用 ArrayList(int initialCapacity)
构造方法显示指定 ArrayList
的初始容量;或者可以通过手动调用 ensureExplicitCapacity(int minCapacity)
方法指定扩容长度,降低默认扩容频率
ArrayList 有缩容吗?
ArrayList
没有缩容。无论是 remove
方法还是 clear
方法,它们都不会改变现有数组 elementData
的长度。但是它们都会把相应位置的元素设置为 null
,以便垃圾收集器回收掉不使用的元素,节省内存。
公众号
觉得对你有帮助可以关注我的公众号,一起学习,一起进步