前言:
-
这里我用的JDK8
-
在讲ArrayList之前,先讲简单讲下List
-
List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引,它继承Collection接口,可以定义一个允许重复的有序集合
-
List接口的特点:
1、有下标
2、有顺序
3、能重复 -
实现List接口的集合有:
- ArrayList、LinkedList、Vector、Stack
-
在这篇文章,只详细讲ArrayList底层
ArrayList
- 实现了List接口,也是最常用的一种集合
- 特点:底层数据结构是数组,查询快,增删除慢,线程不安全,效率高
- 优点:操作读取操作效率高,底层是基于
数组
实现的,可以为null值,可以允许重复元素,有序,异步。 - 缺点:由于它是底层是动态数组实现的,不适合频繁的对元素的进行插入和删除操作,因为每次插入和删除都需 要移动数组中的元素,操作慢且复杂。
底层原理
我们以下的代码进行讲解,且从debug模式进入底层
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<Integer> arryayList = new ArrayList<Integer>();
arryayList.add(1);
arryayList.add(2);
arryayList.add(3);
arryayList.add(4);
arryayList.add(5);
arryayList.add(6);
arryayList.add(7);
arryayList.add(8);
arryayList.add(9);
arryayList.add(10);
arryayList.add(11);
}
}
我们在main方法里面的全部代码都打上断点
进入debug模式,按F7,再按F8,再按F7,就可以跳过类加载器,进入ArrayList类中
- ArrayList中的构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- elementData是一个Object空数组,
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA:是一个空常量数组
1.transient Object[] elementData;
#
2.private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
第一个结论:
Object[] elementData:就是ArrayList存储数据的地方,且是空数组
ArrayList存储数据的地方其实是一个Object数组,并且一开始是{}(空数组)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
接着我们将debug进入add底层,首先会进入包装方法,这个方法与底层无关,跳过,所以我们按F8跳出,再按F7进入
跳到这里add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
size刚开始是等于0 : size=0,
- 我们再进入ensureCapacityInternal方法
这里的minCapacity就是size+1
minCapacity = 1
private void ensureCapacityInternal(int minCapacity) {//minCapacity=1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- 进入calculateCapacity(elementData, minCapacity)
elementData:就是Object数组,这里是空数组
minCapacity = 1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
在ArrayList的构造方法中就是
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
所以两者是相等的,会执行
//DEFAULT_CAPACITY=10
//minCapacity = 1
return Math.max(DEFAULT_CAPACITY, minCapacity);//返回DEFAULT_CAPACITY=10
而DEFAULT_CAPACITY =10
private static final int DEFAULT_CAPACITY = 10;
所以执行的代码如下
return Math.max(10,1);//返回10
那么10就会返回到这里
private void ensureCapacityInternal(int minCapacity) {//minCapacity=1
//相等于ensureExplicitCapacit(10)
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- 进入ensureCapacityInternal方法中
- 此时的minCapacity=10
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
minCapacity=10,
elementData.length = 0
那么必然进行grow(minCapacity)方法中
grow()就是扩容方法
- 进入grow()方法中
private void grow(int minCapacity) {//minCapacity=10
// overflow-conscious code
int oldCapacity = elementData.length;//oldCapacity是旧的容量为0
//newCapacity :新容量
//oldCapacity >> 1:右移一位,位运算,相当于oldCapacity/2
//newCapacity=旧容量+旧容量的0.5倍 = 1.5倍,所以ArrayList每次扩容1.5倍
//但在这里newCapacity=0,因为oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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 oldCapacity = elementData.length;//oldCapacity是旧的容量为0
-
newCapacity :新容量
-
oldCapacity >> 1:右移一位,位运算,相当于oldCapacity / 2
-
**newCapacity=旧容量+旧容量的0.5倍 = 1.5倍,所以ArrayList每次扩容1.5倍**
-
int newCapacity = oldCapacity + (oldCapacity >> 1);
newCapacity = 0 +(0 >> 1)=0
符合if (newCapacity - minCapacity < 0)判断语句
而minCapacity = 10,那么得到newCapacity = minCapacity = 10;
第二次总结:
ArrayList每次扩容原来的1.5倍
- 接着进入copyOf方法,
//original=elementData:就是旧数组
//newLength = newCapacity = 10 //新数组
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
- 里面还有个copyOf,接着进入
最重要的数组复制方法
//将原来的数组,从0开始,复制到copy这个数组中,也是从0开始,复制原来数组的长度
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
将原来的数组,从0开始,复制到copy这个数组中,也是从0开始,复制原来数组的长度
其实就是把原来数组的数据复制到新数组中
第三次总结:
在grow()方法中的Arrays.copyOf()才是做真正的扩容
Arrays.copyOf(elementData,newCapacity(minCapacity))
这个方法在做数组的扩容,底层其实就是System.arrayCopy()
然后返回copy这个新数组,赋值给旧数组,这样就完成扩容
elementData = Arrays.copyOf(elementData, newCapacity);//elementData = copy
完成扩容后回来原来的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 完成扩容
//elementData:就是完成扩容后的数组,size++,
//elementData[1] = e,完成添加操作
elementData[size++] = e;
return true;
}
最后总结
1.
ArrayList存储数据的地方其实是一个Object数组,并且一开始是{}(空数组)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2.
int newCapacity = oldCapacity + (oldCapacity >> 1);
>>1相当于 /2
每次扩容为旧容量的1.5倍
3.
Arrays.copyOf(elementData,newCapacity(minCapacity))
这个方法在做数组的扩容,底层其实就是System.arrayCopy()
4.
首次添加元素,ArrayList中数组长度会被初始化为10
扩容的次数列举
第一次扩容为 10
第二次扩容为 添加第11个元素的时候,把数组的长度扩容为15
第三次扩容 添加第16个元素的时候,数组的长度扩容为22
出处:https://blog.csdn.net/xiannbi
本文版权归作者所有,欢迎转载,须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。