浅谈ArrayList的底层扩容的原理
Posted clover-forever
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈ArrayList的底层扩容的原理相关的知识,希望对你有一定的参考价值。
ArrayList扩容机制的源码详解
一:ArrayList的构造函数:
ArrayList的构造函数源码有三种:
先来看看ArrayList底层定义的一些变量的含义:
/** Default initial capacity
* 默认的容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/** Shared empty array instance used for empty instances.
* 定义的空的数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* 不可以被序列化的数组
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
* 这个list集合的长度
* @serial
*/
private int size;
/**
* Constructs an empty list with an initial capacity of ten
* 空的构造函数
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_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
* 根据用户传入的容量大小构造一个list集合,长度可以大于等于0,但是如果为负数会抛出异常
*/
public ArrayList(int initialCapacity) {
// 如果初始容量大于0
if (initialCapacity > 0) {
// 创建一个大小为initialCapacity的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 创建一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 如果为负数,直接抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 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
* 构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
* 如果指定的集合为null,throws NullPointerException。
*/
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的扩容机制
主要来分析一下无参的构造函数:先来看看add()方法
1:add()方法的实现
// 将指定的元素加到列表的末尾
public boolean add(E e) {
// 添加元素之前,先调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!
// 这里看到的ArrayList添加元素的实质相当于为数组赋值
elementData[size++] = e;
return true;
}
2:ensureCapacityInternal方法的实现
// 得到最小的扩容量
private void ensureCapacityInternal(int minCapacity) {
// 当一开始是默认空的列表
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 获取默认的容量和传入参数的最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
例如:当add进第一个元素时候,minCapacity为1,此时去二者的最大值
3:比较ensureExplicitCapacity
// 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 调用grow方法进行扩容
grow(minCapacity);
}
我们来将上面的内容梳理一下,会get到几个点:
*当我们add进第一个元素到ArrayList时, elementData.length 为0 (因为还是一个空的 list) ,但是此时执行了ensureCapacityInternal()方法,通过默认的比较,此时会得到minCapacity为10。此时minCapacity - elementData.length > 0满足,所以会进入grow(minCapacity)方法.
*当add第二个元素时, minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。
*同理添加第3,4,5,6.....一直到11个元素都不会grow,当到第11个元素的时候,minCapacity(11)比10大,会进行grow操作
4:grow()方法(扩容核心)
// 需要分配的数组大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
// 集合的容量
int oldCapacity = elementData.length;
// 新的集合的容量(在这里运用了位运算,位运算是计算机最快的,右移一位,所以新容量是1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量小于添加的集合的容量,则把该容量替换
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
/** 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 * MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则, * 新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
*/
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 将原数组copy到新的数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
/** 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 * MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则, * 新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
补充:System.arrayCopy()和Arrays.copyOf()方法
联系: 看两者源代码可以发现 copyOf() 内部实际调用了 System.arraycopy() 方法
区别: arraycopy() 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组,并返回该数组。
参考原文:
https://www.cnblogs.com/baichunyu/p/12965241.html
以上是关于浅谈ArrayList的底层扩容的原理的主要内容,如果未能解决你的问题,请参考以下文章