ArrayList源码解析
ArrayList默认的容量为10个Object元素
private static final int DEFAULT_CAPACITY = 10;
ArrayList中包含的元素数量
private int size;
elementData被transient修饰,ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData
为什么不直接用elementData来序列化?原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上述的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而提高效率,节省空间和时间
transient Object[] elementData; // non-private to simplify nested class access
初始化ArrayList实例,则elementData={}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
}
新增元素
List list = new ArrayList();
list.add(“a1”);
第一次新增元素e=“a1”
public boolean add(E e) {
/** 确定是否需要扩容,如果需要,则进行扩容操作*/
ensureCapacityInternal(size + 1); // Increments modCount!!
// eg1:size=0,elementData[0]="a1",然后a自增为1
elementData[size++] = e;
return true;
}
计算ArrayList的容量
如果elementData数组中没有已存储的元素,则返回默认值10,否则返回minCapacity
elementData:底层存储ArrayList元素的数组
minCapacity:ArrayList中的元素个数
size:
// eg1:第一次新增元素,elementData={} minCapacity=1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// eg1:满足if判断,DEFAULT_CAPACITY=10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// eg1:第一次新增元素,所以size=0,则:minCapacity=size+1=1
private void ensureCapacityInternal(int minCapacity) {
// eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
注意:ArrayList在刚开始初始化的时候,容量是0;只有在放入第1个元素的时候,容量才是10。背后的逻辑是,创建ArrayList之后并不知道是否会使用,如果初始化就分配容量,会造成对内存的浪费;所以只有在放入元素的时候才会分配内存
明确ArrayList的容量
minCapacity:ArrayList所需的最小容量
private void ensureExplicitCapacity(int minCapacity) {
// eg1: modCount++后,modCount=1
modCount++;
/** 如果所需的最小容量大于elementData数组的容量,则进行扩容操作 */
if (minCapacity - elementData.length > 0) { // eg1:10-0=10,满足扩容需求
// eg1:minCapacity=10
grow(minCapacity);
}
}
扩容:新增oldCapacity的一半整数长度作为newCapacity的额外增长长度
// eg1:第一次新增元素,minCapacity=10,即:需要将elementData的0长度扩容为10长度。
private void grow(int minCapacity) {
/** 原有数组elementData的长度*/
int oldCapacity = elementData.length; // eg1:oldCapacity=0
/** 新增oldCapacity的一半整数长度作为newCapacity的额外增长长度 */
int newCapacity = oldCapacity + (oldCapacity >> 1); // eg1:newCapacity=0+(0>>1)=0
/** 新的长度newCapacity依然无法满足需要的最小扩容量minCapacity,则新的扩容长度为minCapacity */
if (newCapacity - minCapacity < 0) {
// eg1:newCapacity=10
newCapacity = minCapacity;
}
/** 新的扩容长度newCapacity超出了最大的数组长度MAX_ARRAY_SIZE huge:巨大的 */
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
/** 扩展数组长度为newCapacity,并且将旧数组中的元素赋值到新的数组中 */
// eg1:newCapacity=10, 扩容elementData的length=10
elementData = Arrays.copyOf(elementData, newCapacity);
}
删除元素
public E remove(int index) {
/** 校验传入的参数index是否超出了数组的最大下标,如果超出,则抛出:IndexOutOfBoundsException异常*/
rangeCheck(index);
/** 集合的修改次数加1 */
modCount++;
// eg1:String oldValue="a1"
/** 获得index下标对应的旧值oldValue */
E oldValue = elementData(index);
// eg1:numMoved=4-0-1=3
/** 获得需要移动元素的个数 */
int numMoved = size - index - 1;
if (numMoved > 0) {
/**
* {"a1","a2","a3","a4"}
*
* 假设删除"a2",那么index=1, numMoved=2, 那么下面的语句执行的就是:
* 将第一个入参的elementData从第2位"a3"(index+1)开始复制2位(numMoved)即:"a3","a4";
* 复制到第三个入参elementData的第1位(index),即elementData变成 {"a1","a3","a4","a4"}
*/
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
/** 通知jvm将之前的最后一位元素进行垃圾回收 */
elementData[--size] = null; // clear to let GC do its work
/** 返回已被删除的元素 */
return oldValue;
}
LinkList源码解析
结点
transient Node<E> first;
// 指向最后一个结点
transient Node<E> last;
将新添加的元素e作为链表的最后一个元素,并维护进去
void linkLast(E e) {
final Node<E> l = last;
// eg1: newNode null<--"a1"-->null
/** 创建一个e的Node节点,前置指向原last节点,后置指向null */
final Node<E> newNode = new Node<>(l, e, null);
/** 将newNode节点赋值为last节点 */
last = newNode;
// eg1: l=null
if (l == null) {
/** 如果是第一个添加的元素,则first指针指向该结点*/
first = newNode; // eg1: first指向newNode
} else {
/** 如果不是第一个添加进来的元素,则更新l的后置结点指向新添加的元素结点newNode*/
l.next = newNode;
}
size++;
modCount++;
}
Node传的值
private static class Node<E> {
E item; // 结点元素
Node<E> next; // 后置结点指针
Node<E> prev; // 前置结点指针
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
从链表中删除x结点的链接
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
/** x.prev为null,表示x结点为第一个元素*/
if (prev == null) {
first = next; // 更新first头指针为x结点的后置结点
} else {
prev.next = next; // 将x的前置结点与x的后置结点相连接
x.prev = null; // 断开x的前置指针
}
/** x.next为null,表示x结点为最后一个元素*/
if (next == null) {
last = prev;
} else {
next.prev = prev; // 将x的后置结点与x的前置结点相连接
x.next = null; // 断开x的后置指针
}
x.item = null;
size--;
modCount++;
return element;
}
删除元素
public E remove(int index) {
/** 校验传入的参数index是否超出了数组的最大下标且下标不为负数,如果超出,则抛出:IndexOutOfBoundsException异常*/
checkElementIndex(index);
// eg1:node(index)返回需要删除的结点,即:"a1"
return unlink(node(index)); /** 断开待删除结点的链接 */
}