这里写目录标题
ArrayList
著作:lss 路漫漫其修远兮,可不至于代码。
简介
ArrayList 实现了 List 接口是一个顺序容器,底层是由数组实现,允许放入 null 元素。简之:有序,有下标。该类并没有同步机制。存在线程安全的问题。
以下代码是基于 jdk1.8 源码所写。
构造方法
在看构造方法之前,先考虑一个问题。 ArrayList 的默认容量 (capacity) 是多少呢? 为什么这么说,因为很多小伙伴在谈及这个问题的时候都会不假思索的说是 10。那到底是多少呢 ? 请带着这个疑问往下走。
ArrayList( int initialCapacity )
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// ArrayList 的容量为 initialCapacity
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// ArrayList 的容量为 0
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList ( )
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
// ArrayList 的容量为 0
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList ( Collection<? extends E> c)
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 当传的集合不为 null 将拷贝到一个新的集合
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 初始容量为 0
this.elementData = EMPTY_ELEMENTDATA;
}
}
看完里面的构造方法后,emmmm… 10呢??? 其实在创建 ArrayList 的时候,默认长度是0。解决了上一个疑惑以后, 请进行思考第二个问题。 大家都说初始容量是 10 。构造方面里面明明没有10 。那么这个是 10 的传说是如何来的 ? 带着疑惑继续往下看。
添加方法 add
add(E e)
// 默认下的 数组实列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList 的元数据
transient Object[] elementData;
// 默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// ArrayList 的长度
private int size;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public boolean add(E e) {
// 为了确保 ArrayList 有足够容量
ensureCapacityInternal(size + 1);
// 存储数据
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 在 默认的容量数据中 和 minCapacity 取最大值
/** 10 是这个时候来的。假设我这次是第一次添加。此时 elementData 是 空数组
* 此时 minCapacity = Math.max( 10, size +1 )
*/
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 明确 ArrayList 的容量
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 操作记录 用于并发写的情况下
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 获取 ArrayList 的长度
int oldCapacity = elementData.length;
// 获取新的 ArrayList 的长度
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 保持原由长度
newCapacity = minCapacity;
// 新的长度 > Integer.MAX.VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0)
/**
* 解析 为什么 ArrayList 的最大容量是 Integer.MAX.VALUE - 8 而不是 Integer.MAX.VALUE
* 因为 ArrayList是由数组实现,数组是需要 8 bytes 存储自己的大小。
*/
newCapacity = hugeCapacity(minCapacity);
// 扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
看完这段添加方法, 才看到了那个传说中的 10 。 也就是说其实数组默认的初始容量是 0 ,在第一次添加的时候进行了扩容。扩容后才达到容量为 10。
Arraylist 每次扩容都是原来的 1.5 倍 扩容的时候需要通过 Arrays.copyOf() 方法完成新数组的创建和元素赋值,代价非常高。所以当使用的时候,可以提前预知数据时,可以通过有参构造 或使用 ensureCapacity(int minCapacity) 方法 指定容量。
add( int index, E element )
public void add(int index, E element) {
// 校验下标
rangeCheckForAdd(index);
// 这块和上面的 add 方法是一样的流程
ensureCapacityInternal(size + 1); // Increments modCount!!
// 采用 arraycopy 的方式将 index 位置挪动出来
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 插入数据
elementData[index] = element;
size++;
}
删除方法 remove
remove( int index )
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);
// 将最后一个下标置空, 原因:明确告诉 GC 清理
elementData[--size] = null;
return oldValue;
}
remove( Object o )
public boolean remove(Object o) {
// 删除 null 元素
if (o == null) {
// size.for
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
// 这里和上面的删除一样 都是通过 System.arraycopy
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
获取方法 get
public E get(int index) {
// 校验下标
rangeCheck(index);
// 返回数据
return elementData(index);
}