ArrayList 解析
ArrayList
类似于动态数组,与Vector
的区别主要是Vector
是线程安全的。
Vector
每次扩容请求其大小的 2 倍空间,而 ArrayList
是 1.5 倍。
1. 类定义:
实现了 RandomAccess 接口,因此支持随机访问。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
数组默认大小为10:
private static final int DEFAULT_CAPACITY = 10;
2. 扩容:
添加元素时使用ensureCapacityInternal()
方法来保证容量足够,如果不够时,需要使用 grow()
方法进行扩容,新容量的大小为 oldCapacity + (oldCapacity >> 1)
,也就是旧容量的 1.5 倍。
扩容操作需要调用 Arrays.copyOf()
(Array类的静态方法)把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。
3. 删除:
因为是数组,删除操作时间复杂度是O(N)
, 源码:
public E remove(int index) {
rangeCheck(index); // 如果index越界,拋越界异常
modCount++; // 记录ArrayList 结构发生变化的次数
E oldValue = elementData(index); // 保留,结尾返回
int numMoved = size - index - 1; // 需要ModCount移动的元素的数量
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
4. modCount:
其中的modCount
引人注意,modCount 用来记录 ArrayList 结构发生变化的次数。
-
modCount 定义在抽象类 AbstractList中,结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。
-
在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了就抛出
ConcurrentModificationException
异常,在单线程下也会抛出这这个异常,并非必须在并发下。 -
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。
-
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。(这里就用modCount来实现fail-fast)
比如序列化前后会比较modCount,如modCount增长则抛出异常:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
LinkedList 解析:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
基于双向链表实现,使用 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;
}
}
每个LinkedList实例存储了 first 和 last 指针:
transient Node<E> first;
transient Node<E> last;
LinkedList 结构图:
LinkedList 同样也维护 modCount 实例变量,改变结构时也会增加modCount。
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}