ArrayList是可变长的数组,在实际开发中十分常用,通过分析源码和解析之后记录一下自己的理解。
ArrayList允许存入null;
ArrayList线程不同步;
继承(实现)关系
extends
|---AbstractList<E>
implements
|---List<E>
|---RandomAccess
|---Cloneable
|---java.io.Serializable
字段
//序列化的版本号
private static final long serialVersionUID = 8683452581122892189L;
//默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
//以含参构造函数创建且指定长度为0时使用的数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//以空参构造函数创建时使用的数组实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放元素的数组的引用
transient Object[] elementData;
//实际存放元素的个数
private int size;
构造方法
1.无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
解析:在使用无参构造创建ArrayList实例时,底层数组引用elementData会指向长度为0的数组实例DEFAULTCAPACITY_EMPTY_ELEMENTDATA,此时的容量为0。
2.含参构造①
public ArrayList(int initialCapacity) {
//如果指定的初始容量大于0,实例化一个长度为initialCapacity的数组,并赋值给elementData
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
/* 如果指定的初始化容量等于0,底层数组引用elementData会指向长度为0的数组实例
* EMPTY_ELEMENTDATA
*/
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果指定的初始化容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
解析:该构造方法可以指定初始容量,传入的参数initialCapacity即为初始容量。
3.含参构造②
public ArrayList(Collection<? extends E> c) {
//将集合c转化为数组,赋值给elementData
elementData = c.toArray();
//如果数组长度不等于0
if ((size = elementData.length) != 0) {
//如果得到的数组不是一个Object[]类型的数组,将其转化为Object[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
else {
//如果数组长度为0,element指向EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
解析:该构造方法可以构造一个包含指定集合元素的集合。
动态增长机制:(扩容过程)
以添加一个元素的方法add(E e)为例分析ArrayList的动态增长机制
1.调用add(E e)方法增加1一个元素。
2.在add(E e)方法内部会调用ensureCapacityInternal方法。判断集合是否是以空参构造实例化的,如果是,将默认的初始容量(DEFAULT_CAPACITY=10)作为需要的最小长度;如果不是,则以size+1作为需要的最小容量。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
3.在ensureCapacityInternal方法内部调用ensureExplicitCapacity方法。判断最少需要的容量是否超过了当前数组的长度。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
4.如果最少需要的容量是否超过了当前数组的长度,调用grow方法进行扩容。尝试将原容量的1.5倍作为新的容量newCapacity ,如果新的容量newCapacity还是小于需要的最小容量minCapacity,那就将需要的最小长度minCapacity作为新的容量。接着判断新的容量是否大于MAX_ARRAY_SIZE(Integer.MAX_VALUE-8),如果超过了,就将Integer.MAX_VALUE作为新容量,否则将MAX_ARRAY_SIZE(Integer.MAX_VALUE-8)作为新容量,将原数组赋值到长度为newCapacity的新数组中,此时数组长度才真正发生了改变,扩容完成。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
主要方法
1.add(E e):添加一个元素到末尾 O(1)
public boolean add(E e) {
//判断是否扩容
ensureCapacityInternal(size + 1);
//将元素存放到末尾
elementData[size++] = e;
return true;
}
2.add(int index,E e):将元素添加到指定的位置 O(n)
public void add(int index, E element) {
//判断是否越界
rangeCheckForAdd(index);
//判断是否需要扩容
ensureCapacityInternal(size + 1);
//将将index位置之后的元素后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素存放到index位置上
elementData[index] = element;
size++;
}
3.addAll(Collection<? Extends E> c):将一个集合中的元素按迭代顺序添加到末尾
public boolean addAll(Collection<? extends E> c) {
//将集合c转为数组a
Object[] a = c.toArray();
//记录数组长度numNew
int numNew = a.length;
//判断是否需要扩容
ensureCapacityInternal(size + numNew);
//将数组a的复制到原数组或扩容后的数组中
System.arraycopy(a, 0, elementData, size, numNew);
//更新元素个数size
size += numNew;
return numNew != 0;
}
4.addAll(int index, Collection<? Extends E> c):将一个集合中的元素按迭代顺序添加到指定位置
public boolean addAll(int index, Collection<? extends E> c) {
//判断index是否越界
rangeCheckForAdd(index);
//将集合c转为数组a
Object[] a = c.toArray();
//记录数组长度numNew
int numNew = a.length;
//判断是否需要扩容
ensureCapacityInternal(size + numNew);
//计算需要移动的元素数量numMoved
int numMoved = size - index;
//如果numMoved>0,将index位置及其之后的元素后移numMoved个位置
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将a数组中的元素赋值到从index位置开始的numMoved个位置上
System.arraycopy(a, 0, elementData, index, numNew);
//更新元素个数size为size+numNew
size += numNew;
return numNew != 0;
}
5.remove(int index):删除指定位置上的元素 O(n)
public E remove(int index) {
//判断index是否越界
rangeCheck(index);
modCount++;
//将index位置上的元素记录下来
E oldValue = elementData(index);
//将index位置之后的元素前移(覆盖index位置上元素)
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将末尾元素置空(=null)
elementData[--size] = null;
//返回记录的index位置上的元素(oldValue)
return oldValue;
}
6.remove(Object o):删除第一个出现的指定的元素
public boolean remove(Object o) {
//o如果为null,遍历数组并删除第一个为空的元素
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//o如果不为null,遍历数组并删除第一个与o相同的元素
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
//如果没有找到该元素,返回false
return false;
}
7.removeAll(Collection<?> c):删除指定集合中包含的所有元素
public boolean removeAll(Collection<?> c) {
//判断集合c是否为null,如果为null,抛出NullPointerException
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
//维护两个指针r,w,r用来遍历数组elementData,w用来记录不删除的元素存放位置
int r = 0, w = 0;
boolean modified = false;
try {
//遍历数组elementData中所有元素,依次判断是否存在于集合c中,如果没有,将其复制到w指针位置,w指针后移。
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
//判断r和w的位置,如果r没有移动到末尾(产生了异常中断了程序),将r指针及其之后的位置上的n元素向前移动到w指针位置,w指针后移n个位置。
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//将w指针及其之后的元素置为null
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
8.set(int index,E e):将指定位置上的元素替换为指定元素 O(1)
public E set(int index, E element) {
//判断index是否越界
rangeCheck(index);
//将index位置上的元素记录下来
E oldValue = elementData(index);
//将e覆盖到index位置
elementData[index] = element;
//返回记录的index位置上的元素
return oldValue;
}
9.get(int index):获取指定位置上的元素
public E get(int index) {
//判断index是否越界
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
//返回指定位置上的元素
return (E) elementData[index];
}
10.clear():删除所有元素
public void clear() {
//修改计数+1
modCount++;
// 将所有元素置空
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
11.trimToSize():将集合容量修改为当前元素个数
public void trimToSize() {
//修改计数+1
modCount++;
//如果元素个数小于容量
if (size < elementData.length) {
//如果元素个数为0,将数组置为EMPTY_ELEMENTDATA,如果元素个数不为0,将数组长度调整为与元素个数相同
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
12.indexOf(Object o):返回此集合中指定元素的第一次出现的索引,如果此列表不包含该元素,返回-1。
public int indexOf(Object o) {
//如果o为null,遍历数组并返回第一个为null的元素的位置
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//如果o不为null,遍历数组并返回第一个与o相同的元素的下标
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到,返回-1
return -1;
}
13.lastIndexOf(Object o):返回此集合中指定元素的最后一次出现的索引,如果此列表不包含该元素,返回-1。
public int lastIndexOf(Object o) {
//如果o为null,反序遍历数组并返回第一个为null的元素的位置
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
//如果o不为null,反序遍历数组并返回第一个与o相同的元素的下标
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到,返回-1
return -1;
}
14.contains(Object o):如果此列表包含指定的元素,则返回 true。
public boolean contains(Object o) {
调用indexof(Object o),返回结果与0比较,返回比较结果。
return indexOf(o) >= 0;
}