本文基于JDK1.8
0x 00 总体描述
ArrayList是基于数组实现的,并且支持自动扩容,相对于普通数组而言,由于他自动扩容的的特性,在日常开发过程中,使用的十分多。1
2
3
4// ArrayList.java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, java.io.Serializable
{
从类图和源码可知,ArrayList实现了四个接口:
java.util.List
接口,主要数组的接口类,提供数组的基本操作,如添加、删除、修改、遍历;
java.util.RandomAccess
接口,提供快速随机访问的能力。
java.io.Serializable
接口,表示ArrayList可序列化。
java.lang.Cloneable
表示其支持克隆
继承了java.util.AbstractList
抽象类,是List接口的基本实现,减少了具体实现类中的大部分共性代码的实现。
注:ArrayList重写了大部分AbstractList的方法实现,所以其对ArrayList作用不大,主要是其他减少了其他子类的实现。
0x01 源码阅读
属性
ArrayList的属性很少,只有两个,如下:
1 | // ArrayList.java |
elementData 属性
:元素数组,存储元素。size属性
:数组的大小。size表示的是elementData数组存储的元素数量,并且正好是元素添加到数组中的下标,ArrayList的真实大小是elementData的大小。
构造方法
ArrayList一共三个构造方法如下:
① #ArrayList(int initialCapacity)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
* 带构造方法,主要用于容量的初始化
*/
public ArrayList(int initialCapacity) {
// 初始容量大于0 创建Object数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
// 等于0 使用其自身的final空数组EMPTY_ELEMENTDATA
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
// 小于0 则抛出参数不合法异常
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
该构造方法可以根据传入的初始化容量大小,创建对应的ArrayList数组。对于我们已经明确知道容量大小的场景,可以使用这个构造方法,可以减少数组的扩容,提高系统的性能,合理使用内存。’
② #ArrayList()
无参构造方法,是我们使用最多的构造方法。
1 | // ArrayList.java |
此处需要注意的是,创建的是为空的数组,为了节约内存,在初始化时为空数组,在添加第一个元素时,会真正初始化为10的数组。至于为什么要和EMPTY_ELEMENTDATA
区分开来呢,是为区分起点,空数组是从0开始按照1.5呗倍扩容,而不是10,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA
首次扩容为10;
③ #ArrayList(Collection<? extends E> c)
从参数类型可以看出,传的参数为集合类型,作为elementData。
1 | //ArrayList.java |
普通方法
① 扩容与缩容方法
#trimToSize()
数组的手动缩容方法,实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17ArrayList.java
**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
// 记录修改次数
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
// 大小为0则使用空数组
? EMPTY_ELEMENTDATA
// 大于0则创建大小为size的新数组,将原数组复制进去
: Arrays.copyOf(elementData, size);
}
}
#grow()
扩容方法,在扩容过程中,首先创建一个新的更大的数组,一般时1.5倍大小,将原数组复制到新数组中,最后返回新数组。代码如下:
1 | ArrayList.java |
② 查找指定元素对应的下标
#indexOf(Object o)
1 | ArrayList.java |
③ 克隆方法
#clone()
1 | // ArrayList.java |
④ 转换为数组
#toArray()
and #toArray(T[] a)
1 | // ArrayList.java |
由于可能返回新的数组,所以这里建议使用时还是这么写:a = list.toArray(a)
⑤ 获取指定位置元素
#get(int index)
1 | // ArrayList.java |
⑥ 设置指定位置元素
#set(int index, E element)
1 | // ArrayList.java |
⑦ 添加单个元素
#add(E e)
顺序添加某个元素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
32
33// ArrayList.java
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
// 扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将新建的元素添加到末尾
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
// 增加数组的修改次数
modCount++;
// overflow-conscious code
// 数组扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
#add(int index, E element)
在某个确定位置添加元素1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// ArrayList.java
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
// 范围检查
rangeCheckForAdd(index);
// 数组扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 数组元素拷贝 空出index位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 对应位置赋值
elementData[index] = element;
// size增加1
size++;
}
⑧ 移除元素
#remove(int index)
1 | // ArrayList.java |