ArrayList源码分析
一、ArrayList简介
每个ArrayList实例都有一个capacity 。 容量是用于存储列表中元素的数组的大小。 它始终至少与列表大小一样大。 随着元素被添加到 ArrayList,它的容量会自动增长。 除了添加元素具有恒定的摊销时间成本之外,没有指定增长政策的细节。
应用程序可以在使用ensureCapacity操作添加大量元素之前增加ArrayList实例的容量。 这可以减少增量重新分配的量。
请注意,此实现不是同步的。 如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常是通过同步一些自然封装的对象来完成的列表。 如果不存在这样的对象,则应使用Collections.synchronizedList方法“包装”该列表。 这最好在创建时完成,以防止对列表的意外不同步访问
二、相关属性
1、默认初始化容量为10,调用ensureCapacity进行扩充容量
/**
* 如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳由最小容量参数指定的元素数
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//2、三种构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list //初始化ArrayList为指定初始化大小
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
/**
* 构造一个初始容量为 10 的空列表。
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造一个包含指定集合的元素的列表,按照集合的迭代器返回的顺序。
*
* @param c其元素将被放入此列表的集合
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) { //判断是否为ArrayList的对象
elementData = a; //直接将数据域进行引用
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array. 替换为已经声明的空数组
elementData = EMPTY_ELEMENTDATA;
}
}
public static <T,U> T[] copyOf(U[] original,
int newLength,
Class<? extends T[]> newType)
/**复制指定的数组,用空值截断或填充(如有必要),以便复制具有指定的长度。 对于原始数组和副本都有效的所有索引,两个数组将包含相同的值。 对于在副本中而不是原件有效的任何索引,副本将包含null 。 当且仅当指定长度大于原始数组的长度时,这些索引才会存在。 最终的数组是newType 。
参数类型
U - 原始数组中对象的类
T - 返回数组中对象的类
参数
original - 要复制的数组
newLength - 要返回的副本的长度
newType - 要返回的副本的类
结果
原始数组的副本,截断或填充空值以获取指定的长度 */
3、方法解读
/**
*对于列表大小大于实际大小时进行的大小减小操作,修剪当前列表
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
扩容,判断是否为空的,如果不为初始化时最小扩容为0,如果一开始就为空指定大小,则最小扩容为10
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY; //10
//扩容大于最小扩容时
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
4、添加一个元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 进行增加一个单位
elementData[size++] = e;
return true;
}
三、扩容机制
//添加一个元素到ArrayList
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 增加容量 !!
elementData[size++] = e;
return true;
}
//确保显式容量的扩充
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); //minCapacity最小容量
}
//计算容量 DEFAULTCAPACITY_EMPTY_ELEMENTDATA为空参构造方法时对的数据域指向
private static int calculateCapacity(Object[] elementData, int minCapacity //数据域的最小容量) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //计算是否为默认的初始化为10的空数 据域
return Math.max(DEFAULT_CAPACITY, minCapacity); //默认容量和要拓展容量的大小
}
return minCapacity;
}
//显示拓展数组大小
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //操作数++
// overflow-conscious code
if (minCapacity - elementData.length > 0) //最小的容量减去数据域的容量大于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); //获取最大拓展
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); //复制数据域的方法
}
//最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow //校验最小容量
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //最小容量比最大能开辟的数组大,则返回Integer.MAX_VALUE,其次返回MAX_ARRAY_SIZE
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//添加一个元素到ArrayList的指定索引处
public void add(int index, E element) {
rangeCheckForAdd(index);
//确保增加容量进行+1
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//是否合法的操作
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
//抛出数组越界异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//从指定位置插入集合
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;//判断要移动的位数
if (numMoved > 0) //如果要进行在原数组内添加,将原数组索引值后的数组,移动到其后的位置
//从指定索引值处,其后的元素向后移动指定的位数
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将a数组复制到数据域的指定位置,到新数组的长度+索引值位置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
//本地函数
/**
src数据源 srcpos,索引开始处,dest要移动的目的地,destPos目的地的位置,length总共要移动的长度
*/
public static native void arraycopy(Object src , int srcPos,
Object dest, int destPos,
int length);
二、生活感悟
生活上平平淡淡,原本想复习四级,然后同时学一下集合的源码,突如其来的项目又是给了我当头一棒,照这个样子下去,肯定是过不了四级了,感觉专业课也可能都要挂了,嗐,话说这两天的风真是大给我的心都刮得凉凉的,谁知道以后怎么样呢,抓紧时间变得更强,然后去做一些自己喜欢的的事情。加油,莴苣菜!!!