ArrayList类是List接口的实现类,ArrayList可以看成一个动态数据,可以根据大小动态变化。由于其数组的底层结构,所以它的访问速度非常快,删除和插入操作较慢。
一、 结构图
下图是ArrayList类在Collection集合框架中的结构图,蓝线代表继承关系,绿线代表接口实现
- 继承AbstractList,实现了List。提供了添加、删除、修改、遍历等功能
- 实现RandomAccess接口,提供了随机访问功能,也就是通过索引快速访问数组元素
- 实现Cloneable接口,重写clone( )函数,可以被复制
- 实现java.io.Serializable接口,支持序列化,能通过序列化去传输
二、 ArrayList类简介
1. 定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList支持泛型
2. 属性
// 版本号,不用管
private static final long serialVersionUID = 8683452581122892189L;
// 数组默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// 用于创建一个空的实例
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于保存List数据的数组
private transient Object[] elementData;
// 数组大小
private int size;
ArrayList中有两个重要的属性:elementData 和 size
elementData是Object[]类型的数组
elementData前面有个transient关键字,Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
3. 构造函数
// 构造器一
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
// 构造器二
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
// 构造器三
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);
}
- 第一个构造器:根据指定大小initialCapacity,初始化elementData数组大小
- 第二个构造器:未指定数组大小,则elementData为空数组EMPTY_ELEMENTDATA
- 第三个构造器:将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。
4. 常用API方法摘要
// Collection中定义的API
boolean add(E object)
boolean addAll(Collection<? extends E> collection)
void clear()
boolean contains(Object object)
boolean containsAll(Collection<?> collection)
boolean equals(Object object)
int hashCode()
boolean isEmpty()
boolean remove(Object object)
boolean removeAll(Collection<?> collection)
boolean retainAll(Collection<?> collection)
int size()
<T> T[] toArray(T[] array)
Object[] toArray()
// AbstractCollection中定义的API
void add(int location, E object)
boolean addAll(int location, Collection<? extends E> collection)
E get(int location)
int indexOf(Object object)
int lastIndexOf(Object object)
ListIterator<E> listIterator(int location)
ListIterator<E> listIterator()
E remove(int location)
E set(int location, E object)
List<E> subList(int start, int end)
// ArrayList新增的API
Object clone()
void ensureCapacity(int minimumCapacity)
void trimToSize()
void removeRange(int fromIndex, int toIndex)
从方法列表中可以发现:
- remove() 方法有两个,传参不同返回值也不同,一个根据数组元素值删除返回是否成功,一个根据索引位置删除返回删除的值
- 同样add()、remove()也有两个
三、 源码分析
1. 数组扩容
//
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判断是否需要扩容,并扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 获取当前数组大小
int oldCapacity = elementData.length;
// 获得扩容后的数组大小:原来的1.5倍
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);
}
- 每次在进行插入操作时都会调用ensureCapacity()确保是否需要进行扩容操作,扩容时,每次变为原来的1.5倍大小
2. 添加元素
// 添加单个元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 进行扩容检查
elementData[size++] = e;
return true;
}
// 在指定位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // 扩容检查
// 将index位置后面的数组元素统一后移一位,把index位置空出来
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;
ensureCapacityInternal(size + numNew); // 扩容检查
// 复制数组,将C添加到elementData尾部
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
// 在指定位置添加一个集合
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length ;
ensureCapacity( size + numNew); // 扩容检查
int numMoved = size - index;
// 数组复制,空出第index到index+numNum的位置,即将数组index后的元素向右移动numNum个位置
if (numMoved > 0)
System. arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 将要插入的集合元素复制到数组空出的位置中
System. arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
- 指定位置添加元素时,会将该指定位置后面的元素依次后移
- 可以返现,ArrayList的开销主要都在于数组扩容和数组拷贝
3. 删除元素
// 读取索引位置的元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
// 根据索引位置删除
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
// 复制移动数组,覆盖index位置
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 把移动后空出的位置置为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;
}
// 快速删除
private void fastRemove(int index) {
modCount++;
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
}
- 通过删除方法,可以发现ArrayList中允许存储NULL值
- 通过元素值删除时,只删除查找到的第一个
- 快速删除fastRemove(int index)和remove(int index)的不同在于返回值
- 但添加元素时有扩容操作,但是删除元素时却没有缩容操作,那如果数组被删除到很小,那数组中大量空间将会被闲置,这时候ArrayList提供了trimToSize()方法,可以将数组大小设置为当前size。不过这个方法需要手动自己调用,ArrayList中的其他方法不会调用。
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
4.其他方法
ArrayList类中很多其他方法,比较简单基本上都是通过for循环进行数组的操作,就不进行分析了。
四、 总结
- ArrayList是一种通过动态数组实现的List
- 查找开销是O(1)级别,插入和删除是O(n)级别
- ArrayList会在每次添加新元素时,自己进行扩容操作,扩展为原来的1.5倍大小