JDK1.8 ArrayList部分源码分析小记
底层数据结构
底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。
我们对ArrayList类的实例的所有的操作底层都是基于数组的。
继承与实现关系
ArrayList继承的父类为:AbstractList(抽象类)
实现的接口有:List(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)
友情提示:因为ArrayList实现了RandomAccess接口,所以尽量用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,后者在效率上要差一些
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
部分属性
类的属性中核心的属性为elementData,类型为Object[],用于存放实际元素,并且被标记为transient,也就意味着在序列化的时候,此字段是不会被序列化的。
友情提示:ArrayList的默认容量为10
// 缺省容量
private static final int DEFAULT_CAPACITY = 10;
// 元素数组(调用指定初始值的构造函数时elementData的长度会变成指定值)
transient Object[] elementData;
// 空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 缺省空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
常用构造函数
友情提示:创建ArrayList时尽量设置初始大小(使用ArrayList(int initialCapacity)构造函数)
/**
* ArrayList带容量大小的构造函数
*
* @param initialCapacity
*/
//说明:指定elementData数组的大小,不允许初始化大小小于0,否则抛出异常。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {// 初始容量大于0
this.elementData = new Object[initialCapacity];// 初始化元素数组(新建一个数组)
} else if (initialCapacity == 0) {// 初始容量为0
this.elementData = EMPTY_ELEMENTDATA;// 为空对象数组
} else {// 初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
}
/**
* ArrayList无参构造函数。默认容量是10
*/
//说明:当未指定初始化大小时,会给elementData赋值为空集合。
public ArrayList() {
// 无参构造函数,设置元素数组为空
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
常用函数分析
友情提示:
add方法:当容量到达size时进行扩容(add(E e)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增),扩容为当前容量的0.5倍(若果ArrayList的当前容量为10,那么一次扩容后的容量为15)
set方法:调用set方法时会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
get方法:调用get方法时也会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
remove方法:在移除指定下标的元素时,会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后整个数组不被使用时,可以被GC;元素移动时使用的是System.arraycopy()方法
/**
* 添加元素
*
* @param e
* @return
*/
public boolean add(E e) {
//确保elementData数组有合适的大小
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
/**
* 设定指定下标索引的元素值
*
* @param index
* @param element
* @return
*/
public E set(int index, E element) {
// 检验索引是否合法
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
// 赋新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
/**
* 获取指定下标的元素
*
* @param index
* @return
*/
//说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0),
// 值得注意的是,在get函数中存在element函数,element函数用于返回具体的元素
public E get(int index) {
// 检验索引是否合法
rangeCheck(index);
return elementData(index);
}
// Positional Access Operations
/**
* 位置访问操作
*
* @param index
* @return
*/
//说明:返回的值都经过了向下转型(Object -> E(泛型))
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
/**
* 移除指定下标元素
*
* @param index
* @return
*/
//说明:remove函数用户移除指定下标的元素
// 此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,可以会被GC。
//提醒:元素移动时使用的是System.arraycopy()方法
public E remove(int index) {
// 检查索引是否合法
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
// 需要移动的元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
// 赋值为空,有利于进行GC
elementData[--size] = null; // clear to let GC do its work
// 返回旧值
return oldValue;
}
/**
* 在指定下标位置插入元素
* @param index
* @param element
*/
public void add(int index, E element) {
//检查索引是否合法
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
其他方法介绍
友情提示:rangeCheckForAdd方法用于add(int index, E element)和addAll(int index, Collection<? extends E> c)方法中检验索引是否合法;rangeCheck方法用于get、set等方法中检验索引是否合法(因为不改变数据结构,故index不能取到size,最大只能取到size-1)
//检验索引是否合法(只校验了上限)(index不能等于size(index<size))
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//检验索引是否合法(校验了上限和下限)(index可以等于size(0<=index<=size))
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
扩容策略
// 确定ArrarList的容量。
// 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;//默认容量10
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//确保elementData数组有合适的大小
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判断元素数组是否为空数组
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取较大值
}
//确保elemenData数组有合适的大小
ensureExplicitCapacity(minCapacity);
}
//确保elemenData数组有合适的大小
private void ensureExplicitCapacity(int minCapacity) {
// 结构性修改加1
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//对数组进行扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;// 旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1);// 新容量为旧容量的1.5倍
if (newCapacity - minCapacity < 0)// 新容量小于参数指定容量,修改新容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大于最大容量
newCapacity = hugeCapacity(minCapacity);// 指定新容量
// 拷贝扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
部分方法测试
- add方法
public class AddTest {
@Test
public void test1(){
//我们可以看到,在add方法之前开始elementData = {};
// 调用add方法时会继续调用,直至grow,最后elementData的大小变为10,
// 之后再返回到add函数,把8放在elementData[0]中
List<Integer> lists = new ArrayList<Integer>();
lists.add(8);
}
@Test
public void test2(){
//说明:我们可以知道,在调用add方法之前,elementData的大小已经为6,之后再进行传递,不会进行扩容处理。
List<Integer> lists = new ArrayList<Integer>(6);//elementData.length=6
lists.add(8);
}
}
- rangeCheck方法
public class RangeCheckTest {
@Test
public void test() {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
//该语句报ArrayIndexOutOfBoundsException异常是rangeCheck(index)引发的(index >= size)
System.out.println(list.get(10));
//rangeCheck(index)方法只校验上线,该语句报ArrayIndexOutOfBoundsException异常是elementData[index]引发的
System.out.println(list.get(-1));
list.remove(-1);//同上
Object[] a = new Object[]{1, 2, 3};
System.out.println(a[-1]);
}
}
- indexOf方法
public class IndexOfTest {
@Test
public void test(){
List list = new ArrayList();
list.add(null);
list.add(2);
list.add(2);
list.add(null);
System.out.println(list.indexOf(null));//0
System.out.println(list.indexOf(2));//1
System.out.println(list.indexOf(3));//-1
}
}
- toArray方法
public class ToArrayTest {
@Test
public void test() {
List list = new ArrayList(10);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Object[] array =list.toArray();
//调用Arrays.copyOf()-->调用System.arraycopy()
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。