前言
每当大家谈起ArrayList
都只会关注它的自动扩容机制,但是大多数人却不会去关注ArrayList
是否会自动缩容。下面会根据几个问题让大家了解一下ArrayList
的缩容机制。
PS:缩容指的是ArrayList中的Object数组elementData的大小缩减
ArrayList
是否会自动缩容?
既然ArrayList
的自动扩容一般是发生在add()
和addAll()
方法中,那么当ArrayList
调用remove()
方法时是否会自动缩容呢?
ArrayList#remove()
源码分析
remove()
方法有两个
- 入参为数组下标返回被删除的元素
- 入参为数组元素返回数组下标
PS:本文研究的JDK源码为Oracle JDK 1.8
这里对第1个方法的源码进行分析
public E remove(int index) {
// 校验传入的下标是否大于或等于当前ArrayList的长度size
// 如果超过直接抛出IndexOutOfBoundsException
rangeCheck(index);
// 修改次数自增
// modCount是用于快速失败机制的,这里不展开
modCount++;
// 获取需要删除的元素的值
E oldValue = elementData(index);
// 计算数组从需要删除的元素后需要移动的元素个数
int numMoved = size - index - 1;
if (numMoved > 0)
// 使用System.arraycopy()来进行数组元素左移
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 多余的元素标记为null便于gc回收,同时size自减
elementData[--size] = null;
// 返回被删除的元素值
return oldValue;
}
PS:System#arraycopy()并不会影响源数组和目标数组的长度。
ArrayList#remove()
源码图解
如果觉得源码比较难懂,可以参考下面的ArrayList
的remove()
方法关键过程图解
从上面可以看出ArrayList
在执行remove()
方法时,内置的Object
数组实际上并没有减少长度,只是通过数组部分元素左移、最后一个元素置为null
同时size
自减来实现删除操作。而且入参为数组元素返回数组下标的remove()
、removeAll()
和removeRange()
也是类似的做法。
ArrayList#clear()
源码分析
除了remove()
以外,ArrayList
还有一个clear()
方法用来对数组进行清空。那它又是怎么工作的呢?实际上简单到我不敢相信
public void clear() {
// 修改次数自增
modCount++;
// 遍历数组并置为null便于gc回收
for (int i = 0; i < size; i++)
elementData[i] = null;
// 直接给size赋值为0
size = 0;
}
结论
从上面两节源码的分析可以得知,实际上ArrayList
是不提供自动缩容机制的。其实这样设计也无可厚非,毕竟很多时候都无法判断是否需要进行缩容操作。既然没有自动缩容,那ArrayList可以进行缩容吗?
ArrayList
可以进行缩容吗?
当然是有的,那就是trimToSize()
方法。这个方法会将ArrayList
内置的数组缩容到当前的size
,源码也特别简单,就是给elementData
一个对应size
长度的数组。源码如下
public void trimToSize() {
// 修改次数自增
modCount++;
// 判断当前是否需要缩容
if (size < elementData.length) {
// 如果size为0,直接给elementData赋值内置的空数组
// 不为0则创建一个size长度的新数组
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
总结
-
ArrayList
的remove
只是通过数组部分元素左移、最后一个元素置为null
同时size
自减来实现删除操作。实际上并未对elementData
进行缩容。 - 可以使用
trimToSize()
方法对elementData
进行缩容。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。