近期项目总算稳定下来,有空可以学学数据结构,今天我们挑ArrayList下手。
我们以Java 1.8版本作为分析的基础。
0. 继承结构
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承:AbstractList。
实现:List、RandomAccess、Cloneable、Serializable。
1. 构造方法
1.1 带容量大小
// 可见ArrayList底层使用数组来存储数据。 这不废话吗,名字就是ArrayList.
transient Object[] elementData;
// 一个空数组。
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
1.2 不带任何参数
// 也是一个空数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
1.3 带集合参数
public ArrayList(Collection<? extends E> c) {
// 直接将原来的集合转换为数组,赋值给elementData
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 集合为空
this.elementData = EMPTY_ELEMENTDATA;
}
}
1.4 为何定义两个空数组
我们从1.1和1.2中发现,当不指定容量大小和指定容量大小为0时,使用了两个不同的空数组对elementData进行了赋值,为何这样做呢?
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
官方解释:为了区分后续的添加元素时,elementData初始化大小而这样设计的。
2. add
2.1 add(E e)
public boolean add(E e) {
// 这句代码是关键,涉及首次添加元素的elementData的初始化和扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 元素被添加在数组末尾
// 没有做null检查,元素可以为null
elementData[size++] = e;
return true;
}
ensureCapacityInternal
// 默认初始化大小为10
private static final int DEFAULT_CAPACITY = 10;
// 数组允许分配的最大大小。
// 减8是因为部分虚拟机会保存一些关键字在数组中
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void ensureCapacityInternal(int minCapacity) {
// 传入的minCapacity=当前元素个数+1
// 首次调用时,minCapacity=1
// 如果使用的不带参数的构造函数
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULT_CAPACITY为10.
// 所以首次添加元素时,minCapacity=10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 检查并确保容量
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 当前elementData的容量不满足要求,则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 扩容后新的elementData大小= oldCapacity + (oldCapacity >> 1)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 扩容后仍然不满足需要,则赋值为minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 扩容后超出允许的最大数组大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 确保newCapacity最大只能为Integer.MAX_VALUE
newCapacity = hugeCapacity(minCapacity);
// 分配新数组,拷贝旧数据
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
以上分析可知:
- 如果使用了默认的构造函数,那么elementData初始化大小为10.
- 正常情况下,每次扩容后的elementData新大小=1.5倍旧大小。
- elementData最大值只能为Integer.MAX_VALUE。
2.2 add(int index, E element)
public void add(int index, E element) {
// 首先检查inde是否合法
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// 这个地方主要为了将index及其后的数据后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将元素赋值给数组index处
elementData[index] = element;
size++;
}
2.3 addAll(Collection<? extends E> c)
public boolean addAll(Collection<? extends E> c) {
// 转换为数组
Object[] a = c.toArray();
int numNew = a.length;
// 扩容
ensureCapacityInternal(size + numNew); // Increments modCount
// 拷贝数据
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
3. get
public E get(int index) {
// 检查索引是否合法
rangeCheck(index);
// 返回指定索引处的元素
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
4. remove
4.1
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);
// 赋值为null,保证可以被GC回收
elementData[--size] = null; // clear to let GC do its work
// 返回旧的数据
return oldValue;
}
4.2 remove(Object o)
// 删除元素。
// 只会删除第一个出现的元素。
public boolean remove(Object o) {
if (o == null) {
// for循环遍历元素。找出待删除元素的index
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
// 此处导致只会删除第一个找到的相等元素
return true;
}
} else {
// for循环遍历元素。找出待删除元素的index
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
// 此处导致只会删除第一个找到的相等元素
return true;
}
}
return false;
}
// 该方法与remove(int index)基本相同
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
}
4.3 removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
// 保证参数不能为空
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
// 此处complement=false
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
// for循环导致elementData中0..W处存储的是所有不包含c的元素
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// 防止contains抛出异常
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// 清除元素,使得GC可以回收
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
5. set
public E set(int index, E element) {
// 检查索引
rangeCheck(index);
E oldValue = elementData(index);
// 直接赋值
elementData[index] = element;
// 返回set前的旧数据
return oldValue;
}
6. 总结
ArrayList,Vector主要区别为以下几点:
- Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;
- ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
- Vector可以设置capacityIncrement,而ArrayList不可以,从字面理解就是capacity容量,Increment增加,容量增长的参数。