一、定义
(一)概要
ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了Collection和List接口,灵活的设置数组的大小等好处。
(二)继承关系
// 类继承关系
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.ArrayList<E>
// 所有已实现的接口
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
// 直接已知子类
AttributeList, RoleList, RoleUnresolvedList
(三)类图
二、常用方法
(一)构造方法
// 构造一个初始容量为 10 的空列表
ArrayList()
// 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的
ArrayList(Collection<? extends E> c)
// 构造一个具有指定初始容量的空列表
ArrayList(int initialCapacity)
(二)方法
// 将指定的元素添加到此列表的尾部
boolean add(E e)
// 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部
boolean addAll(Collection<? extends E> c)
// 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
boolean addAll(int index, Collection<? extends E> c)
// 移除此列表中的所有元素
void clear()
// 返回此 ArrayList 实例的浅表副本
Object clone()
// 如果此列表中包含指定的元素,则返回 true
boolean contains(Object o)
// 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数
void ensureCapacity(int minCapacity)
// 返回此列表中指定位置上的元素
E get(int index)
// 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
int indexOf(Object o)
// 如果此列表中没有元素,则返回 true
boolean isEmpty()
// 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1
int lastIndexOf(Object o)
// 移除此列表中指定位置上的元素
E remove(int index)
// 移除此列表中首次出现的指定元素(如果存在)
boolean remove(Object o)
// 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
protected void removeRange(int fromIndex, int toIndex)
// 用指定的元素替代此列表中指定位置上的元素
E set(int index, E element)
// 返回此列表中的元素数
int size()
// 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
Object[] toArray()
// 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型
<T> T[] toArray(T[] a)
// 将此 ArrayList 实例的容量调整为列表的当前大小
void trimToSize()
三、源码解析(JDK1.6.0_20)
1.定义成员变量
// 定义序列号ID
private static final long serialVersionUID = 8683452581122892189L;
// 定义底层数组
private transient Object[] elementData;
// 定义集合大小
private int size;
2.定义构造函数
// 默认无参构造函数,其实调用的是有参构造函数
public ArrayList() {
this(10);
}
// 指定集合大小有参构造函数
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0){
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
his.elementData = new Object[initialCapacity];
}
// 入参为集合类型的构造函数
public ArrayList(Collection<? extends E> c) {
// 将集合转为数组
elementData = c.toArray();
// 给集合大小赋值
size = elementData.length;
// c.toArray返回的不一定是Object[]类型数组(比如:List<String> list = Arrays.asList("abc");)
if (elementData.getClass() != Object[].class) {
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
}
3.扩容和新增元素
// 数组扩容
public void ensureCapacity(int minCapacity) {
// 增加集合修改次数
modCount++;
// 原有集合长度
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
// 每次扩容为本身的0.5倍
int newCapacity = (oldCapacity * 3)/2 + 1;
// 如果原有集合本身小,然后一次新增元素很多则长度为:原有集合长度+新增集合长度
if (newCapacity < minCapacity){
newCapacity = minCapacity;
}
// 数组扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
// 增加单个元素
public boolean add(E e) {
// 扩容
ensureCapacity(size + 1);
// 在新增元素放入输入指定位置
elementData[size++] = e;
return true;
}
// 指定位置增加元素
public void add(int index, E element) {
if (index > size || index < 0) {
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
ensureCapacity(size+1); // 扩容
// 将原有数组指定位置到数组最后一个元素,整体向后面移动一个位置
System.arraycopy(elementData, index, elementData, index + 1, size - index);
// 将新增元素放入指定位置
elementData[index] = element;
size++;
}
// 将集合作为参数,新增至原有集合
public boolean addAll(Collection<? extends E> c) {
// 将集合变为数组
Object[] a = c.toArray();
// 传入数组长度
int numNew = a.length;
// 扩容
ensureCapacity(size + numNew); // 注意这里传入的是原有数组和新增数组长度之和
System.arraycopy(a, 0, elementData, size, numNew); // 将新增数组放入原有数组后面(从最后一个元素开始)
// 原有集合大小增加
size += numNew;
return numNew != 0;
}
4.移除、替换、获取元素
// 删除指定位置元素
public E remove(int index) {
// 效验移除的元素是否存在
RangeCheck(index);
// 增加集合修改次数
modCount++;
// 移除的目标元素
E oldValue = (E) elementData[index];
// 集合指定下标元素后面元素的个数
int numMoved = size - index - 1;
// 将指定元素后面所有的元素向前面移动一个位置
if (numMoved > 0){
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
// 将数组最后一个元素置空
elementData[--size] = null;
return oldValue;
}
// 删除指定对象
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;
}
// 清除所有的元素
public void clear() {
modCount++;
// 移除所有元素,并将集合大小置为0
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
// 将指定位置元素替换
public E set(int index, E element) {
RangeCheck(index);// 参数效验
// 获取指定下标元素
E oldValue = (E) elementData[index];
// 将指定下标元素替换为element
elementData[index] = element;
return oldValue;
}
// 获取指定下标位置元素
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];// 返回指定下标元素
}
5.其他
// 将集合大小调整为实际元素个数
public void trimToSize() {
modCount++;
// 底层数组长度
int oldCapacity = elementData.length;
// 将集合大小置为实际元素个数大小
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
// 返回集合元素个数
public int size() {
return size;
}
// 判断元素个数是否为0
public boolean isEmpty() {
return size == 0;
}
// 判断指定元素是否存在
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 返回指定元素下标,不存在则返回-1
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; // 默认返回元素不存在
}
// 倒序检查集合中是否存在目标对象
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 浅克隆目标集合
public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
// 将集合转为数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
// 将集合复制进入目标数组中(空的)
public <T> T[] toArray(T[] a) {
if (a.length < size){
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
}
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size){
a[size] = null;
}
return a;
}
四、总结
1.ArrayList底层为一个动态的数组,通过操作数组来实现各种操作的,在新建一个ArrayList集合时候最好根据实际使用情况初始化集合的大小,避免频繁的扩容影响程序性能。
2.将数组转为集合时候,尽量不要使用List list = Arrays.asList(array),这样获取的集合在list.toAarry()后可能获取的不是Object[],当写入集合时类型不是底层数组保存的类型时候会报错。而采用List list = new ArryList(Arrays.asList(array))这样的形式,这样在ArryList参数为Collection类型的构造函数里面会将底层数组类型置为Object[]。
3.ArrayList不是同步的,如果需要同步请采用Vector、Collections类中提供的静态工厂方法创建的类或者考虑使用并发容器类CopyOnWriteArrayList。
4.其抽象类AbstractList已经实现了Iterable接口,并返回了一个Iterator接口类型的直接实现类如:Iterator iter = list.iterator();可以通过迭代器实现ArrayList集合的遍历(遍历时候不能通过list引用本身对集合进行操作,但是可以通过迭代器),也可以通过新for循环和传统for循环进行遍历。
5.大家在阅读源码时候有一个参数modCount(集合修改次数),这个参数是在ArrayList父类AbstractList中定义的,有什么用?我们知道ArrayList的迭代器也是在其父类AbstractList中实现的,我们在迭代遍历时候如果在多线程环境下有其他线程改变了这个list那么迭代器会抛出ConcurrentModificationException异常,怎么抛出的呢?就是在调用next()、remove()等方法时候会去检查这个modCount参数是否被改变了,如果被改变则抛出ConcurrentModificationException异常(fail-fast机制)。