ArrayList源码详解
ArrayList UML类图
ArrayList 概述
ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的。实现了所有可选列表操作,并允许包括 null 在内的所有元素。
除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。默认初始容量为 10。随着 ArrayList 中元素的增加,它的容量也会不断的自动增长。在每次添加新的元素时,ArrayList 都会检查是否需要进行扩容操作,扩容操作带来数据向新数组的重新拷贝,所以如果我们知道具体业务数据量,在构造 ArrayList 时可以给 ArrayList 指定一个初始容量,这样就会减少扩容时数据的拷贝问题。当然在添加大量元素前,应用程序也可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量,这可以减少递增式再分配的数量。
请注意,此实现不同步。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。所以为了保证同步,最好的办法是在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(…));
ArrayList 源码分析
几个比较重要的字段
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//共享的空数组实例。
private static final Object[] EMPTY_ELEMENTDATA = {};
//存放元素的数组
private transient Object[] elementData;
transient是个关键字,这个关键字的意思是:transient修饰的变量将不进行序列化
详解:http://blog.csdn.net/u013207877/article/details/52572975
EMPTY_ELEMENTDATA 和elementData字段的作用将在构造方法里体现
构造方法
//初始化一个默认为10的容量
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
//可以指定初始化容量,指定初始容量为负时,则抛出异常
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
//使用集合类对象初始化ArrayList,底层使用数组copy方式。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
//jdk源码注释: c.toArray might (incorrectly) not return Object[] (see 6260652)
//意思是:c.toArray可能(错误)不返回Object[] see 6260652 这个编号代表JDK bug库中的编号
//原因:https://www.cnblogs.com/cmdra/p/5901793.html
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
add方法
boolean add(E e)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // jdk源码注释:Increments modCount!!。 自增修改次数!!
elementData[size++] = e;
return true;
}
//这个方法是 确保内部的容量大小可以存储元素
private void ensureCapacityInternal(int minCapacity) {
//if语句判断的是,当前是否第一次添加元素,如果是的话,则把最小容量(minCapacity)赋值为10
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY的值一定是10,final修饰了
}
ensureExplicitCapacity(minCapacity);
}
//这个方法是 确保明确增加多少容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//防止迭代的时候有其他线程进行结构的改变。
//如果最小的列表容量跟当前数组的长度相减大于0的话,那么需要进行扩容;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//这个方式是进行容量扩容
private void grow(int minCapacity) {
//oldCapacity 旧的容量
int oldCapacity = elementData.length;
//newCapacity 新的容量 (oldCapacity >> 1)这个表达式的意思是oldCapacity*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果if语句成立,当minCapacity小于0则会抛出内存溢出异常,当minCapacity大于0,则newCapacity=Integer.MAX_VALUE;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//当前元素添加到新的数组,并进行数组扩容。
elementData = Arrays.copyOf(elementData, newCapacity);
}
//数组扩容并把元素添加到新的数组里面
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
//判断类型是否一致,如果不一致,则重新实例化(Array.newInstance())数组
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//复制数组,将原数组从0索引开始复制,复制到新的数组,从0索引位置开始复制,复制Math.min(original.length, newLength)个
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
void add( int index, E element)方法
//指定的位置插入指定的元素,指定的位置必须小于等于size
public void add(int index, E element) {
//范围检查,指定的索引不能超过当前的容量值,也不能小于0
rangeCheckForAdd(index);
//确保容量内部有位置存储
ensureCapacityInternal(size + 1); // Increments modCount!!
//将当前数组从指定的索引开始复制,复制到指定索引后面的一个位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//检查传入的index值是否越界。
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
Remove
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);
//将最后一个位置置空
elementData[--size] = null; // JVM会清除
//返回移除的元素
return oldValue;
}
get(int index)
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
trimToSize
底层数组的容量调整为当前列表保存的实际元素的大小的功能
public void trimToSize() {
modCount++;
//如果时间大小小于缓冲区容量的长度,则进行数组复制。
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
Iterator迭代器
private class Itr implements Iterator<E> {
int cursor; // 返回下一个元素的索引
int lastRet = -1; // 返回的最后一个元素的索引;如果没有,则为1
int expectedModCount = modCount;
public boolean hasNext() {
//第一次调用,因为变量的初始化,那么cursor的值为0,
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
//检查是否有被并发修改
checkForComodification();
//将光标赋值给i
int i = cursor;
//如果光标值大于等于集合个数的话,则抛出异常
if (i >= size)
throw new NoSuchElementException();
//获取集合内的元素
Object[] elementData = ArrayList.this.elementData;
//判断是否有其他线程进行修改
if (i >= elementData.length)
throw new ConcurrentModificationException();
//光标加1
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//调用本类的删除方法
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}