CopyOnWriteArrayList源码分析
作用:CopyOnWriteArrayList(Copy-On-Write: 写入时复制)是一个线程安全的ArrayList,对其进行的修改操作都是先加锁然后在底层的一个复制数组上进行。
优点:经常被用于"读多写少"的并发场景,因为读取的时候不需要加锁,性能较好。读写分离,在使用迭代器迭代的时候不会抛出异常。
缺点:需要拷贝原数据,数据较大的时候容易引起频繁Full GC;写和读在不同的数组上,读取的是旧数组的数据。
成员变量
// 独占锁
final transient ReentrantLock lock = new ReetrantLock();
// 只能通过getArray/setArray方法来获取/修改array
private transient volatile Object[] array;
初始化
1. 创建空链表
public CopyOnWriteArrayList() {
setArrayList(new Object[0]);
}
2. 根据给定的集合数据创建链表
public CopyOnWriteArrayList(Collections<? extends E> c) {
Object[] elements;
//如果是CopyOnWriteArrayList类,直接引用
if (c.getClass() == CopyOnWriteArrayList.class) {
elements = ((CopyOnWriteArrayList<?>)c).getArray();
} else {
elements = c.toArray();
//c.toArray可能不会返回Object[]类型
if (elements.getClass() != Object[].class) {
//通过Arrays.copyOf复制数组
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
}
//设置array变量为elements
setArray(elements);
}
部分方法源码
set(int index, E element) 修改array[index]的值
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//拿到成员变量array
Object[] elements = getArray();
// 获取elements[index]的值
E oldValue = get(elements, index);
//如果值发生了变化
if (oldValue != element) {
int len = elements.length;
//先对elements复制一个拷贝
Object[] newElements = Arrays.copyOf(elements, len);
//更新值
newElements[index] = element;
//更新this.array = newElements
setArray(newElements);
} else {
//为了保证volatile语义
setArray(elements);
}
//返回原始值
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
add(E e) 添加元素到末尾
//和上面的set差不多 流程: 先获取锁->拷贝数组->修改拷贝数组->修改原始数组引用
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;
//修改array引用
setArray(newElements);
return true;
} finally {
//释放锁
lock.unlock();
}
}
add(int index, E element) 按照下标位置添加
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;//计算添加位置下标
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);//末尾直接复制一个长度+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();
}
}
remove(int index) 删除index位置的值
public E remove(int index) {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//index位置的值
E oldValue = get(elements, index);
//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);
//修改array引用
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
emoveRange(int fromIndex, int toIndex) 根据范围删除元素
public void removeRange(int fromIndex, int toIndex) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 如果开始下标<0 或者结束下标大于数组长度或者结束下标小于开始下标
// 抛下标越界异常
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
// 计算新数组长度
int newlen = len - (toIndex - fromIndex);
// 判断结束下标是否是最后一个下标
int numMoved = len - toIndex;
if (numMoved == 0)
//复制原数组
setArray(Arrays.copyOf(elements, newlen));
else {
//新建一个长度为newlen的数组
Object[] newElements = new Object[newlen];
//将除了需要删除的下标元素复制到新数组
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
retainAll(Collection<?> c) 在当前集合保留传入集合中的元素,当前元素和集合中元素做交集
public boolean retainAll(Collection<?> c) {
//集合为null抛出空指针异常
if (c == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取当前集合对象中数组
Object[] elements = getArray();
int len = elements.length;
//当前数组不为空
if (len != 0) {
// temp array holds those elements we know we want to keep
int newlen = 0;
//创建中间数组
Object[] temp = new Object[len];
//遍历数组
for (int i = 0; i < len; ++i) {
Object element = elements[i];
//判断集合c中是否包含elements中的元素
if (c.contains(element))
//包含则将元素存入中间数组
temp[newlen++] = element;
}
//中间数组下标+1之后不等于elements数组长度
if (newlen != len) {
//使用newlen作为新数组长度复制
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
clear()方法 清空集合
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//指向一个新建长度为0的数组
setArray(new Object[0]);
} finally {
lock.unlock();
}
}
addAll(int index, Collection<? extends E> c) 按照下标位置批量添加元素
public boolean retainAll(Collection<?> c) {
//集合为null抛出空指针异常
if (c == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取当前集合对象中数组
Object[] elements = getArray();
//获取数组的长度
int len = elements.length;
//如果当前数组不为空
if (len != 0) {
// temp array holds those elements we know we want to keep
int newlen = 0;
//创建一个临时数组
Object[] temp = new Object[len];
//遍历数组
for (int i = 0; i < len; ++i) {
Object element = elements[i];
//判断集合c中是否包含elements中的元素
if (c.contains(element))
//包含则将元素存入临时数组
temp[newlen++] = element;
}
//如果临时数组是否等于elements数组长度
if (newlen != len) {
//不等于则使用newlen作为新数组长度复制
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
return false;
} finally {
lock.unlock();
}
}