add方法理解ArrayList的扩容机制
目录
- ArrayList的构造方法(前置知识)
- ArrayList的add方法(理解扩容机制)
- add 添加元素
- 得到最小扩容量
- 判断是否需要扩容
- 扩容方法
ArrayList的构造方法(前置知识)
可快速过
一些基本成员变量:
// 默认初始大小 private static final int DEFAULT_CAPACITY = 10; // 空数组 用于空实例 private static final Object[] EMPTY_ELEMENTDATA = new Object[0]; // 用于默认大小空实例的共享空数组实例。 // 我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0]; // 保存ArrayList数据的数组 transient Object[] elementData; // 大小 private int size; // 数组的最大长度 private static final int MAX_ARRAY_SIZE = 2147483639;
构造方法:
/** * 默认初始容量大小 */ private static final int DEFAULT_CAPACITY = 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** *默认构造函数,使用初始容量10构造一个空列表(无参数构造) */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 带初始容量参数的构造函数。(用户自己指定容量) */ 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); } } /** *构造包含指定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方法(理解扩容机制)
add 添加元素
/** * 将指定的元素追加到此列表的末尾。 */ public boolean add(E e) { // 添加元素之前,先试探,容量够不够 // Increments modCount!! 这里有个failfast机制 // 简而言之就是并发场景下通过modcount的值判断当前的操作是否是最新的modcount的数据 ensureCapacityInternal(size + 1); //这里看到ArrayList添加元素的实质就相当于为数组赋值 elementData[size++] = e; return true; }
得到最小扩容量
我们接下来看看这个“试探容量的方法”ensureCapacityInternal()
怎么实现的
我们约定一个值,也就是传参进来的size+1,叫minCapacity
(最小需要容量)
//得到最小扩容量 就是你至少要扩容至这么多,才能容纳下我的数据 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 获取默认的容量和传入参数的较大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
当要add进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity为10。
判断是否需要扩容
然后是ensureExplicitCapacity()
:
//判断是否需要扩容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) //调用grow方法进行扩容,调用此方法代表已经开始扩容了 grow(minCapacity); }
我们来梳理一下:
- 当我们要 add 进第 1 个元素到 ArrayList 时,
elementData.length
为0 (因为还是一个空的 list),因为执行了ensureCapacityInternal()
方法 ,所以minCapacity
此时为 10。此时,minCapacity - elementData.length > 0
成立,所以会进入grow(minCapacity)
方法,此时数组容量变为10 - 当 add 第 2 个元素时,minCapacity 为 2,此时 elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,
minCapacity - elementData.length > 0
不成立,所以不会进入 (执行)grow(minCapacity)
方法。 - 添加第 3、4···到第10个元素时,依然不会执行 grow()方法,数组容量都为10。
- 直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大。进入 grow 方法进行扩容。
扩容方法
按照elementData.length
(newCapacity)扩容1.5倍,然后看看够不够minCapacity
。
不够的话,newCapacity
就=minCapacity
hugeCapacity()
比较一下newCapacity
是否超过MAX_ARRAY_SIZE
,超过就赋为Integer.MAX_VALUE
然后就进行扩容,Arrays.copyof
方法
/** * 要分配的最大数组大小 */ 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, //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的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: elementData = Arrays.copyOf(elementData, newCapacity); }
int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)! 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数
tips:
- java 中的
length
属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性. - java 中的
length()
方法是针对字符串说的,如果想看这个字符串的长度则用到length()
这个方法. - java 中的
size()
方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
资料:javaguide
以上就是add方法理解ArrayList的扩容机制的详细内容,更多关于add方法ArrayList扩容的资料请关注我们其它相关文章!
赞 (0)