包路径:package java.util;
一、基本属性
1、private static final long serialVersionUID = 8683452581122892189L;
该属性是序列化的时候使用的,serialVersionUID是用来表明类的不同版本之间的兼容性,若一个序列化类没有声明这样一个static final的常量,JVM会根据各种参数为该类计算一个,对于同一个类,不同的JDK版本可能得出不同的serialVersionUID。
2、private static final int DEFAULT_CAPACITY = 10; 数组的默认初始容量大小
3、private static final Object[ ] EMPTY_ELEMENTDATA = { }; 数组的初始化使用
4、private transient Object[ ] elementData; ArrayList的底层数据结构是数组
transient关键字:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。
5、private int size; 集合中存储元素的个数
6、protected transient int modCount = 0;
在父类AbstractList中定义了该属性:modCount,是用来记录AbstractList结构性变化的次数。在ArrayList所有涉及结构性变化的方法中(增加、删除、修改等)都涉及modCount值++操作。
二、构造函数
1、有参构造如下:传入的参数是数组默认的初始化大小,直接对数组elementData属性进行初始化
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
2、无参构造如下:elementData数组为空
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
3、有参构造如下:传入的参数是一个集合类型的对象
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
三、继承关系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承AbstractList类,实现了List<E>等的接口,ArrayList是List接口的一个实现类。
RandomAccess:该接口是一个标志接口,查看该接口的源码会发现,该接口是一个空的,但是只要List集合实现这个接口,支持快速随机访问,我们即可以通过元素的序号快速获取元素对象。总的来说,实现了这个接口的List,使用for循环获取数据优于使用迭代器获取数据,LinkedList没有实现这个接口,那么对于它来说,使用迭代器获取数据优于使用for循环获取数据。
Cloneable:可以进行克隆,
java.io.Serializable:可以进行序列化和反序列化的操作
四、增长方式
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
默认数组的初始化大小是10,按照原数组大小的1.5倍方式进行扩容增长。
两个if语句相当于是给扩容后的大小定了一个上下界。
五、CRUD
1、增加元素的方法(add、addAll)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
传入的参数是一个:元素
ensureCapacityInternal()方法:首先判断数组是否为空,若为空,数组的容量大小设置为10,确保数组的容量足够存放新加入的元素,若不够,需要进行扩容
将元素插入到elementData数组中,相应的size+1
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
传入的参数是两个:元素和想要添加的位置下标
rangeCheckForAdd():该方法是用来检查传入的位置下标是否合法,不合法抛出下标越界的异常
System.arraycopy():
public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
将数组src从下标srcPos进行拷贝,一直拷贝length个元素到dest数组中,在dest数组中从destPos下标开始加入先前的srcPos数组。
也就是说此处调用这个方法是要把插入的位置空出来,存储插入的元素
addAll()方法同理上述两个传入参数不同的add()方法,只是传入的参数由数据变成了集合类型的对象。
特点:可以存储重复的数据,也可以存储null
2、删除元素的方法(remove、removeAll、removeRange)
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; // clear to let GC do its work
return oldValue;
}
按下标删除,首先检查删除下标的合法性,然后找出要删除下标所对应的元素,使用system.arraycopy()方法进行值的挪动拷贝(覆盖掉要删除的下标元素),删除元素意味着size要相应的减少。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
按元素进行删除,查找元素,找到要删除元素的下标,调用fastRemove()方法,传入删除下标,进行快速删除。
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
从一个起始位置到结束位置之间的元素全部删除,具体就不再解释,该方法未调用别的陌生的方法,
removeAll():该方法同上述的思想,只是传入的参数是一个集合类型的对象,调用了batchRemove方法进行删除。
特点:数组的起始元素下标是0,找到第一个出现的元素直接删除并返回,时间复杂度:O(n)
3、获取当前元素的方法(get)
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
通过下标进行获取,首先检查下标的合法性,通过所以位置直接查询数组elementData相对应的元素,时间复杂度:O(1),查询效率很高。
4、遍历ArrayList中的对象
public Iterator<E> iterator() {
return new Itr();//返回一个内部类对象
}
iterator()方法是在AbatractList类当中实现了的,该方法返回AbstractList的一个内部类Itr对象。
六、总结
1、ArrayList基于数组的方式实现,没有容量的限制(扩容)
2、添加元素可能需要扩容(最好首先预判一下)
3、线程不安全
4、内部类:ArrayList有三个内部类
private class Itr implements Iterator<E>
private class ListItr extends Itr implements ListIterator<E>
private class SubList extends AbstractList<E> implements RandomAccess
Itr实现了Iterator接口,重写了里面的hasNext()、next()、remove()方法。
ListItr继承了Itr而且实现了ListIterator接口,重写了hasPrevious(),nextIndex(),previousIndex(),previous(),set(E e),add(E e)等方法。
SubList继承了AbstractList,实现了RandomAccess接口,类内部实现了对子序列的增删改查等方法,但它同时也充分利用了内部类的优点,就是共享ArrayList的全局变量,例如检查器变量modCount,数组elementData等,所以SubList进行的增删改查操作都是对ArrayList的数组进行的,并没有创建新的数组。