JDK14 中ArrayList的动态扩容简单介绍
一、简介
1.1集合
-
集合与数组的区别:
数组 集合 长度 固定 可变 存储内容 同一类型的元素,可以存储基本数据类型值。 存储的都是对象,而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。 -
集合按照其存储结构可以分为两大类
- 单列集合 java.util.Collection
- 双列集合 java.util.Map
1.2Colleciton接口
-
Collection是所有单列集合的父接口,集合是java中提供的一种容器,可以用来存储多个数据。
-
它有两个重要的子接口,分别是 java.util.List 和 java.util.Set 。
-
List 的特点是元素有序、元素可重复。List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList
-
Set 的特点是元素无序,而且不可重复。Set 接口的主要实现类有 java.util.HashSet 和 java.util.TreeSet。
-
常用功能(接口的所有抽象方法都需要被子类实现)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaISST2v-1627564150953)(图片/image-20210729164729243.png)]
1.3List接口
-
特点:
-
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)
- 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
-
常用方法(接口的所有抽象方法都需要被子类实现)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x7GfRpDG-1627564150955)(图片/image-20210729164925062.png)]
二、ArrayList
2.1概述
ArrayList 是一个可以实现动态扩容的Object数组。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
它继承了AbstractList这个抽象类,实现了List, RandomAccess, Cloneable, java.io.Serializable四个接口。
- ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
- ArrayList *实现了RandmoAccess接口,即提供了随机访问功能。*RandmoAccess是java中用来被List实现,为List提供快速访问功能的。
- 在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
- ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
- ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和Vector不同,ArrayList中的操作不是线程安全的
所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
2.2构造器
// 默认构造函数
ArrayList()
// capacity是ArrayList的默认容量大小。
ArrayList(int capacity)
// 创建一个包含collection的ArrayList
ArrayList(Collection<? extends E> collection)
三、ArrayList的动态扩容简单介绍
Demo
package com.test0729.demo;
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
// AarryList: 动态数组实现,查找快,删除增加慢
ArrayList<Integer> data = new ArrayList<>();
// 无参构造函数:创建一个长度为0的空数组
data.add(100);
// add:只return true
// 第一次add,需要扩容,默认长度为10,所以数组的长度变为10
}
}
无参构造器
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 注释写的是创建一个初始容量为10的空数组,但是DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个长度为0的空数组。
/**
* 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
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
- 注释解释了这样是为了,与EMPTY_ELEMENTDATA区分,在第一次add的时候才会扩容。后面我们会知道第一次扩容的默认大小为10,所以api中才会写"创建一个初始容量为10的空数组"。
有参构造器
add
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
-
方法中首先将变量modCount自增1;
-
modCount是用来标记当前arraylist集合操作变化的次数,在fail-fast机制中会有用到这个变量。
protected transient int modCount = 0;
- 然后调用重载的另一个add方法;
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
- s即传入的size,如果当前数组的大小已经不够用了,就会调用grow()方法扩容;
- 添加e至队尾,并将数量加1。
grow
private Object[] grow() {
return grow(size + 1);
}
- 调用重载的另一个grow方法,并将当前的数组元素的数量+1传入,即minCapacity;
/**
* 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
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
-
oldCapacity :数组的长度
-
minCapacity:数组元素数量+1,即最小需求。
- 如果是add()调用的,根据
s == elementData.length
可知 minCapacity应该是oldCapacity+1; - 如果是其他方法调用,则根据实际情况。
- 如果是add()调用的,根据
-
如果数组不是空数组
扩容的大小为ArraysSupport.newLength()的返回值,实现方法为Arrays.copyOf()。
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth /
oldCapacity >> 1 / preferred growth */);
注意传入值,oldCapacity是数组的长度,minCapacity - oldCapacity应该是数组元素数量+1-数组长度,oldCapacity >> 1 是数组长度的一半。
-
否则,数组是空数组,扩容的大小为DEFAULT_CAPACITY和minCapacity中的最大值。
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
- 如果是初始化的空数组,那么第一次扩容后的大小为默认值10。
ArraysSupport.newLength
/**
* Calculates a new array length given an array's current length, a preferred
* growth value, and a minimum growth value. If the preferred growth value
* is less than the minimum growth value, the minimum growth value is used in
* its place. If the sum of the current length and the preferred growth
* value does not exceed {@link #MAX_ARRAY_LENGTH}, that sum is returned.
* If the sum of the current length and the minimum growth value does not
* exceed {@code MAX_ARRAY_LENGTH}, then {@code MAX_ARRAY_LENGTH} is returned.
* If the sum does not overflow an int, then {@code Integer.MAX_VALUE} is
* returned. Otherwise, {@code OutOfMemoryError} is thrown.
*
* @param oldLength current length of the array (must be non negative)
* @param minGrowth minimum required growth of the array length (must be
* positive)
* @param prefGrowth preferred growth of the array length (ignored, if less
* then {@code minGrowth})
* @return the new length of the array
* @throws OutOfMemoryError if increasing {@code oldLength} by
* {@code minGrowth} overflows.
*/
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// assert oldLength >= 0
// assert minGrowth > 0
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
if (newLength - MAX_ARRAY_LENGTH <= 0) {
return newLength;
}
return hugeLength(oldLength, minGrowth);
}
-
oldLength:数组长度
-
minGrowth:数组元素数量+1-数组长度
-
prefGrowth:数组长度/2
-
newLength初始化为Math.max(minGrowth, prefGrowth) + oldLength;
-
如果newLength小于MAX_ARRAY_LENGTH,那么返回newLenght。
/** * The maximum length of array to allocate (unless necessary). * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * {@code OutOfMemoryError: Requested array size exceeds VM limit} */ public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; /** * A constant holding the maximum value an {@code int} can * have, 2<sup>31</sup>-1. */ @Native public static final int MAX_VALUE = 0x7fffffff;
-
否则,调用hugeLength
hugeLength
private static int hugeLength(int oldLength, int minGrowth) {
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow
throw new OutOfMemoryError("Required array length too large");
}
if (minLength <= MAX_ARRAY_LENGTH) {
return MAX_ARRAY_LENGTH;
}
return Integer.MAX_VALUE;
}
- minLength:数组元素数量+1
- 如果没有溢出,则返回MAX_ARRAY_LENGTH
- 否则抛出异常OutOfMemoryError(“Required array length too large”);
总结
- 使用无参构造器创建的ArrayList初始容量为0,第一次调用add()/addAll()方法时才会初始化数组的容量,初始容量为10。
- 对集合添加若干个元素时,如果当前集合的容量满足需求,不扩容;
- 如果当前集合容量不满足需求,则扩大为原来的1.5倍;如果扩大1.5倍依然不满足需求,则扩大为满足需求的最小容量。