ArrayList扩容机制实现原理(源码分析)
程序员文章站
2022-07-11 12:15:04
...
目录
1.ArrayList的构造函数
构造函数源码
。
private static final int DEFAULT_CAPACITY = 10;//默认容量大小
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//空数组
/**
*1.默认构造函数,使用初始容量10构造一个空列表(无参数构造)
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 2.带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//初始容量大于0
//创建initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {//初始容量等于0
//创建空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {//初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
*3.
*构造包含指定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;
}
}
当无参数构造方法调用的时候,是创建一个空数组,加入第一个数据的时候才会进行扩容。
2.ArrayList的扩容机制
2.1 add()
将元素添加到尾端
public boolean add(E e) {
//添加元素之前,先调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // 确定最小容量
elementData[size++] = e;//size当前添加的位置
return true;
}
2.2 ensureCapacityInternal()
得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 获取默认的容量和传入参数的较大值
//第一次添加数据,minCapacity为1,max之后为10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);//判断是否需要扩容
}
2.3 ensureExplicitCapacity()
判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//需要的最小容量比原数组长,调用扩容方法grow()
if (minCapacity - elementData.length > 0)
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
grow(minCapacity);
}
当添加第一个元素时,空数组0,扩容为10
当添加到第11个时,minCapacity 为11大于第一次扩容后的长度DEFAULT_CAPACITY(10),开始扩容
2.4 grow()
开始扩容
//可以分配的最大数组长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* ArrayList扩容的核心方法。
*/
private void grow(int minCapacity) {
// oldCapacity为旧容量,newCapacity为新容量
int oldCapacity = elementData.length;
//将oldCapacity 右移一位,其效果相当于oldCapacity /2,
int newCapacity = oldCapacity + (oldCapacity >> 1);//将新容量更新为旧容量的1.5倍
//新容量和需求的容量比较:新容量 = max(新容量,需要的容量)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量大于 MAX_ARRAY_SIZE,进入`hugeCapacity()` 方法
//比较 minCapacity 和 MAX_ARRAY_SIZE
// 因为oldCapacity <minCapacity<= newCapacity ,
//如果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:
elementData = Arrays.copyOf(elementData, newCapacity);
}
2.5. hugeCapacity()
比较minCapacity和MAX_ARRAY_SIZE,确定newCapacity
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//对minCapacity和MAX_ARRAY_SIZE进行比较
//若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
//若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
一个简单的判断。
3.总结
ArrayList的初始是空的数组,第一次添加数据的时候数组初始为默认长度10。每一次添加数据的时候,得到最小扩容量,判断是否需要扩容,扩容的长度为旧容量的1.5倍,在判断新容量和最大数组长度,最后完成扩容。
在此,感谢guide哥的技术支持
原文链接链接: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/ArrayList-Grow.md.
上一篇: ArrayList源码分析及扩容机制
推荐阅读
-
PHP strtotime函数用法、实现原理和源码分析
-
RAC cache fusion机制实现原理分析
-
[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
-
深入源码分析Spring注解的实现原理---@Import
-
源码分析 Alibaba sentinel 滑动窗口实现原理(文末附原理图)
-
源码分析 RateLimiter SmoothBursty 实现原理(文末附流程图)
-
源码分析 Sentinel DegradeSlot 熔断实现原理
-
源码分析RocketMQ顺序消息消费实现原理
-
ArrayList扩容机制及不可变性详解(附源码分析)
-
LeakCannary使用方法及实现原理探究(二)—— LeakCannary实现原理及源码分析