目录
2.5 addAll(int index, Collection c)
ArrayList采用数组存储数据,允许重复数据和null值。ArrayList大体和Vector类似,最大的区别是ArrayList非线程安全,Vector线程安全。
1.类结构
ArrayList类层级结构如下图所示:
ArrayList包含的成员变量:
//elementData为Object类型的数组,用于存储数据
transient Object[] elementData; // non-private to simplify nested class access
//表示数组元素个数(不是数组容量)
private int size;
ArrayList包含的常量:
//数组默认初始化容量为10
private static final int DEFAULT_CAPACITY = 10;
//表示空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//也是空数组,和EMPTY_ELEMENTDATA区别开来
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2.方法解析
2.1构造函数
ArrayList():
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
无参构造函数,elementData为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
ArrayList(int initialCapacity):
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//创建容量大小为initialCapacity的ArrayList
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//elementData为EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
//initialCapacity小于0,抛出IllegalArgumentException异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList(Collection<? extends E> c):
public ArrayList(Collection<? extends E> c) {
//将集合c转化为对象数组
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
//如果参数c是ArrayList类型的集合,将对象数组a赋值给elementData
elementData = a;
} else {
//否则将数组a赋值到elementData
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
//a数组为size为0,elementData为EMPTY_ELEMENTDATA
elementData = EMPTY_ELEMENTDATA;
}
}
2.2 add(E e)
add(E e)
用于尾部添加元素:
public boolean add(E e) {
//确定数组容量,size=0
ensureCapacityInternal(size + 1); // Increments modCount!!
//末尾添加元素,然后size递增1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//当使用无参构造方法,并且第一次添加元素时,DEFAULT_CAPACITY=10,minCapacity=1,所以返回10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//否则返回minCapacity,minCapacity为原数组中存储元素的个数(size)+1
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//调用无参构造,第一次添加元素时,minCapacity=10,elementData.length=0,所以调用扩容方法grow()
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//MAX_ARRAY_SIZE常量值为Integer.MAX_VALUE - 8
// 通过这段逻辑我们可以知道,ArrayList最大容量为Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
通过上面源码分析,可以得出以下几个结论:
a.任何一个空的ArrayList在添加第一个元素时,内部数组容量将被扩容为10
b.扩容时,newCapacity为oldCapacity的1.5倍
c.数组容量最大值为Integer.MAX_VALUE
d.默认添加的元素在数组尾部,尾部添加元素不用移动任何元素,所以速度快
2.3 add(int index, E element)
add(int index, E element)用于在指定位置添加元素
public void add(int index, E element) {
//下标检查
rangeCheckForAdd(index);
//确定数组容量,和上面add(E e)方法中调用ensureCapacityInternal()介绍的一致
ensureCapacityInternal(size + 1); // Increments modCount!!
//将原数组中index位置及后面的所有元素往后移动一个位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在数组index位置放入新元素
elementData[index] = element;
//数组元素个数递增
size++;
}
private void rangeCheckForAdd(int index) {
//下标比size大或者下标小于0,都抛出数组下标越界异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
这里涉及到元素移动,所以速度较慢。
2.4 addAll(Collection<? extends E> c)
addAll(Collection<? extends E> c)用于添加指定集合的元素
public boolean addAll(Collection<? extends E> c) {
//将要添加的集合转为对象数组
Object[] a = c.toArray();
//要添加的集合元素个数
int numNew = a.length;
//确定数组容量,和上面add(E e)方法中调用ensureCapacityInternal()介绍的一致
ensureCapacityInternal(size + numNew); // Increments modCount
//直接将新增数组a中所有元素复制到elementData数组中,从a数组的第0的位置开始,a数组中第0个元素复制到elementData数组中的size下标处,一共复制numNew个元素
System.arraycopy(a, 0, elementData, size, numNew);
//更新elementData数组中元素个数,size=size+numNew
size += numNew;
//如果要添加的元素个数newNew不为0,表示添加成功
return numNew != 0;
}
这里涉及到元素移动,所以速度较慢。
2.5 addAll(int index, Collection<? extends E> c)
addAll(int index, Collection<? extends E> c) 在指定位置,添加指定集合元素
public boolean addAll(int index, Collection<? extends E> c) {
//下标合法性校验
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
//原数组eleMentData位于index后面的元素个数
int numMoved = size - index;
if (numMoved > 0)
//将原数组中index位置及后面的所有元素往后移动numNew个位置
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//直接将新增数组a中所有元素复制到elementData数组中,从a数组的第0的元素开始复制,a数组中第0个元素复制到elementData数组中的index下标处,一共复制numNew个元素
System.arraycopy(a, 0, elementData, index, numNew);
//更新elementData数组中元素个数,size=size+numNew
size += numNew;
//如果要添加的元素个数newNew不为0,表示添加成功
return numNew != 0;
}
涉及到元素的移动,速度较慢。
2.6 remove(int index)
remove(int index) 删除指定位置元素
public E remove(int index) {
//下标合法性校验
rangeCheck(index);
//修改次数递增
modCount++;
//获取指定位置的元素(需要被删除的元素)
E oldValue = elementData(index);
//要移动的元素个数,numMoved=数组元素个数(size)-指定下标(index)-1(不包括index位置的这个元素),也可以理解为:获取指定index位置后面的所有元素个数减去1(要删除的元素)
int numMoved = size - index - 1;
//numMoved=0,说明是删除数组的最后一个元素,无需移动位置
if (numMoved > 0)
//直接将指定index位置后面的所有元素往前移动一位,覆盖index处的元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将原素组size位置的元素置空,并将size-1
elementData[--size] = null; // clear to let GC do its work
//返回被删除的元素
return oldValue;
}
涉及到元素的移动,效率不高。
2.7 remove(Object o)
remove(Object o)删除指定元素
public boolean remove(Object o) {
//由于允许元素为null,所以要先判断删除的元素是否为null
if (o == null) {
//遍历数组,找到第一个为null的目标元素,然后调用fastRemove()方法,进行删除
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//遍历数组,找到第一个目标元素,然后调用fastRemove()方法,进行删除
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
//逻辑和remove(index)一致,都是将index后面的元素往前移动一位,覆盖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
}
涉及到元素移动,效率不高。
2.8 get(int index)
get(int index)获取指定位置元素
public E get(int index) {
//下标合法性校验
rangeCheck(index);
//直接返回数组指定位置元素
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
直接返回数组指定下标元素,速度非常快。
2.9 set(int index, E element)
set(int index, E element)设置指定位置的元素为指定值
public E set(int index, E element) {
//下标合法性校验
rangeCheck(index);
//获取指定index位置的元素,旧值
E oldValue = elementData(index);
//将数组index位置设置为新值
elementData[index] = element;
//返回旧值
return oldValue;
}
set
方法不涉及元素移动和遍历,所以速度快。