1. 数据结构
数组(没错就是简单的数组)
2.类的继承关系
public class ArrayList extends AbstractListimplements List, RandomAccess, Cloneable, java.io.Serializable
(ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。)
3.类的属性
private static final long serialVersionUID = 8683452581122892189L;//版本号
private static final int DEFAULT_CAPACITY = 10;//默认大10
private static final Object[] EMPTY_ELEMENTDATA = {};//空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//元素数组
transient Object[] elementData//实际元素大小默认为零
private int size;//ArrayList的大小(它包含的元素的数量)。
4.构造函数
1. ArrayList(Collectionextends extends E> c) {
//参数为集合的构造函数
elementData = c.toArray();
//转化为数组
if ((size = elementData.length) != 0) {
//判断参数是否为空
if (elementData.getClass() != Object[].class) //判断是否成功转化为object数组
elementData = Arrays.copyOf(elementData, size, Object[].class);//不为object数组就进行复制 } else {
this.elementData = EMPTY_ELEMENTDATA;
//设置元素数组为空。 }}
2.public ArrayList(int initialCapacity) {
//参数为int的构造函数
if (initialCapacity > 0) {//初始容量大于0
this.elementData=new Object[initialCapacity]; }//初始化元素数组
else if (initialCapacity == 0)//初始容量为0 {this.elementData = EMPTY_ELEMENTDATA; }
//设置为空对象数组
else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}//初始容量小于0,抛出异常.
3.public ArrayList() {
//无参构造函数,设置元素数组为空
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
5.重要函数
5.1 add函数
public boolean add(E e) {
ensureCapacityInternal(size + 1); elementData[size++] = e;
return true;
}
//内容很简单就是添加元素,我们重点看一下ensureCapacityInternal()
private void ensureCapacityInternal(int minCapacity)
{ ensureExplicitCapacity
(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//结构性修改加1
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//判断elementData是否为空
return Math.max(DEFAULT_CAPACITY, minCapacity);
}//返回较大值
return minCapacity;
}
这几个函数有两个作用
1.赋予elementData合适的大小
2.扩容
此时我们看一下扩容函数grow()
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);
}
//拷贝扩容
正常情况下会扩容1.5倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值。当我们调用add方法时,实际上的函数调用如下
也就是说我们调用add函数,实际调用的函数为
程序调用add,实际上还会进行一系列调用,可能会调用到grow,grow可能会调用hugeCapacity。
接下来用一个例子说明一下
List lists = new ArrayList();
lists.add(9);
我们可以看到,在add方法之前开始elementData = {};调用add方法时会继续调用,直至grow,最后elementData的大小变为10,之后再返回到add函数,把9放在elementData[0]中.
5.2 set函数
public E set(int index, E element) {
rangeCheck(index);
//检查索引是否合法(有没有超出范围)
E oldValue = elementData(index);
//旧值
elementData[index] = element;
//新值
return oldValue;
}
[if !supportLists]5.2 [endif]indexOf函数
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)//遍历数组,找到第一个为空的元素,返回下标
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)//遍历数组,找到第一个和指定元素相等的元素,返回下标
if (o.equals(elementData[i]))
return i;
}
return -1; }
5.3 get函数
public E get(int index) {
rangeCheck(index);//判断索引是否合法
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}//返回值都经过了向下转型onject-E。
5.4 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; 赋值为空,有利于进行GC
return oldValue;
}
remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。
6.fail-fast的原理
1. fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。
具体由Iterator的内部类Itr实现
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
..............
}
cursor是指集合遍历过程中的即将遍历的元素的索引,lastRet是cursor -1,默认为-1,即不存在上一个时,为-1,它主要用于记录刚刚遍历过的元素的索引。expectedModCount这个就是fail-fast判断的关键变量了,它初始值就为ArrayList中的modCount。(modCount是抽象类AbstractList中的变量,默认为0,而ArrayList 继承了AbstractList ,所以也有这个变量,modCount用于记录集合操作过程中作的修改次数,与size还是有区别的,并不一定等于size)迭代器迭代结束的标志就是hasNext()返回false,
public boolean hasNext() {
return cursor !=size;
}
而该方法就是用cursor游标和size(集合中的元素数目)进行对比,当cursor等于size时,表示已经遍历完成。在ArrayList进行add,remove,clear等涉及到修改集合中的元素个数的操作时,modCount就会发生改变(modCount ++),所以当另一个线程(并发修改)或者同一个线程遍历过程中,调用相关方法使集合的个数发生改变,就会使modCount发生变化,这样在checkForComodification方法中就会抛出ConcurrentModificationException异常。
6.1解决方案
1.使用Arraylist迭代器的remove(其中没有modCount ++)。
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();
}
}
2.使用java并发包(java.util.concurrent)中的类来代替。