关于CopyOnWriteArrayList
概述
看这个类名CopyOnWriteArrayList继承ArrayList,通过查看类声明CopyOnWriteArrayList和ArrayList不存在继承关系。两个类的特点:1)都继承List接口,是List接口实现类。2)底层存储数据都采用数组 3)添加元素操作且ArrayList.size>数组长度时,以数组的1.5倍进行扩容;CopyOnWriteArrayList的扩容方式与ArrayList完全不一样。加一个元素copy一个新数组,老元素迁移动新数组中。然后,新增元素赋给新数组中。 4)ArrayList是非线程安全的,CopyOnWriteArrayList是线程安全的。5)ArrayList会预占内存,CopyOnWriteArrayList使用多少申请多少内存。
类图
存储结构
之前讲过,CopyOnWriteArrayList存储结构是数组。array变量数组元素类型使用Object类型,transient元素不能序列化,volatile修饰线程可见性
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
构造函数
CopyOnWriteArrayList有三个构造函数,针对三种使用场景。
1)无参场景,直接new实例就可以
2)如果想Collection实例元素迁移到CopyOnWriteArrayList中,可以使用CopyOnWriteArrayList(Collection<? extends E> c)
3)最后一种是[]数组元素迁移到CopyOnWriteArrayList中,可以使用CopyOnWriteArrayList(E[] toCopyIn)
添加𢆉单元素
首先调用ReentrantLock.lock函数上锁,若其他线程同时进入add函数处于阻塞状态。Arrays.copyOf复制一个Object[]类型新数组newElements。新元素变量e,赋给数组最后一个索引对应的值。最后调用setArray函数设置newElements变量为数组引用,ReentrantLock.unlock释放锁。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
指定索引位置插入元素
首先调用ReentrantLock.lock函数上锁。检查index索引是否越界(index > len || index < 0),若越界抛出IndexOutOfBoundsException异常。计算numMoved,numMoved为0时Array.copyOf复制数组;numMoved不为0时,数组移动分出两部以0到index(不含index)和(index+1到最后)。数组移动完成,index索引位置赋element元素和释放锁。
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
指定索引查找元素
public E get(int index) {
return get(getArray(), index);
}
指定索引𩆋值替换
源码挺简单直接看源码吧
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
writeObject函数
将elements数组元素写入到ObjectOutputStream流中。
readObject函数
从ObjectInputStream流实例对象保存到Object[]数组中,保存的过程会新建一个Object[]数组代替array变量。