ArrayList底层源码分析

image-20240305143529677

底层原理

  • 创建使用ArrayList<>()创建空对象的时候,会在底层创建一个长度为0的数组。该数组的名称为elementData,定义变量size

    • size变量有两层含义
      • ① 表示元素的个数,也就是集合的长度
      • ② 表示下一个元素的存入位置
  • 添加第一个元素之后,size++,同时底层会创建一个新的长度为DEFAULT_CAPACITY=10的数组
  • 扩容机制1:
    • 当存满的时候,会创建一个新的数组,新数组的长度是原来的1.5倍,也就是长度为15。再把所有的元素拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那就会继续按照相同的方式扩容1.5倍。
  • 扩容时机2:
    • 当使用例如addAll()这样的方法一次性添加多个数据,扩容1.5倍的方法不够用时,则以扩容的实际大小为准。

成员变量:

1
2
3
4
5
6
7
8
9
private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData;

private int size;

构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//无参构造器,最开始创建的是空数组,当添加第一个元素时初始化容量为10。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//指定初始化容量,为0的话则创建空数组。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}

//传入一个集合,将该集合中的元素存到ArrayList中。
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}

add方法

  • add(E e):直接在ArrayList尾部追加元素
  • add(int index, E element):在指定index位置追加元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public boolean add(E e) {
//记录结构上被修改的次数
modCount++;
add(e, elementData, size);
return true;
}

private void add(E e, Object[] elementData, int s) {
//如果当前数组长度等于ArrayList容量则扩容
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}

public void add(int index, E element) {
//检查下标是否越界
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
//如果当前数组长度等于ArrayList容量则扩容
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
//把index位置及其后的元素向后移动一位
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}

grow()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//获取新容量大小
//oldCapacity右移一位即是原长度的一半,ArraysSupport.newLength方法选择minCapacity - oldCapacity或oldCapacity >> 1较大的一方与oldCapacity相加。
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
//将之前元素迁移到新数组,返回按照新容量扩容后的数组
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
//如果容量为0,则按照默认容量创建一个数组。
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}

private Object[] grow() {
return grow(size + 1);
}

添加一个元素源码分析

第一次添加数据

添加多个元素源码分析

第11次添加数据