源码解析
CopyOnWriteArrayList 首先它是实现了 List,RandomAccess,Cloneable,Serializable
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
我们主要看看常用的方法add、get、set、remove方法
首先是add方法,每步都有注释
//直接添加一个元素到数组的最后
public boolean add(E e) {
//给当前的对象上锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//首先得到数组
Object[] elements = getArray();
//得到原先数组的长度
int len = elements.length;
//拷贝一份新的数组,长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//把传入的元素放置数组最后一位
newElements[len] = e;
//修改当前对象的初始值
setArray(newElements);
return true;
} finally {
//释放锁
lock.unlock();
}
}
//根据下标进行添加元素
public void add(int index, E element) {
//上锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//如果下标大于当前数组长度或者小于0 则抛出下标越界异常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
//得出要插入的下标位置
int numMoved = len - index;
//为0表示在数组最后,直接插入
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
// 否则表示要插入数组中间,直接创建一份新的数组
newElements = new Object[len + 1];
// 先拷贝index前部分的内容,在拷贝index后部分的内容
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//在把元素存入当前下标位置
newElements[index] = element;
//修改当前对象的初始值
setArray(newElements);
} finally {
//释放锁
lock.unlock();
}
}
在来看看set方法
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();
}
}
再来看看get方法,
//调用了下面的私有方法
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
private E get(Object[] a, int index) {
return (E) a[index];
}
最后是remove方法
public E remove(int index) {
//上锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//得到当前数组
Object[] elements = getArray();
//得到当前的长度
int len = elements.length;
//得到需要删除下标的值
E oldValue = get(elements, index);
int numMoved = len - index - 1;
//如果是在最后一位直接删除
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
//如果不在最后一位,那么就重新拷贝一份
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
//修改当前对象的初始值
setArray(newElements);
}
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
总结
可以看到,其中增删改操作都是上了锁的,因此CopyOnWriteArrayList主要是作用在读多写少的场景下,缺点就是如果是需要写上立马就能读到的,那么不建议用这个,因为读是从旧的副本读取的,而写时存储的数据是重新拷贝了一份新的数组了,从而可能导致数据的不一致性。
网友评论