一、概述
CopyOnWriteArrayList是ArrayList的一个线程安全变体。它被优化为读取而不是写入的列表。
当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。这确保所有读取是一致的且线程安全。
使用CopyOnWriteArrayList的情况通常是当你对列表的读取次数多于写入次数时。它通常在多个线程需要同时并发读取相同数据的情况下使用,但每次只有一个线程需要写入列表。CopyOnWriteArrayList在写操作方面比ArrayList慢,由于涉及复制,但在读操作方面要快得多,并且在必须保证线程安全的情况下是一个不错的选择。
二、源码阅读
package Thread;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//定义一个ReentrantLock类型的锁(不可被改变、反序列化)
final transient ReentrantLock lock = new ReentrantLock();
//定义一个Object类型的数组(私有、反序列化)
private transient volatile Object[] array;
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
//定义一个getArray方法,获得原数组(不可改变)
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
//定义一个setArray方法,给传入的数组赋值(不可改变),用于覆盖原数组
final void setArray(Object[] a) {
array = a;
}
//无参构造,将数组初始化为空数组
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
//忽略 unchecked 警告信息
@SuppressWarnings("unchecked")
//get方法获取指定下标元素
private E get(Object[] a, int index) {
return (E) a[index];
}
//get方法重载,内部调用getArray方法
public E get(int index) {
return get(getArray(), index);
}
//set方法给指定下标设置值,并且返回旧值:复制新数组,在新数组中给指定下标改值,再覆盖原数组
public E set(int index, E element) {
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//获取原数组
Object[] elements = getArray();
//获取当前数组指定下标位置的元素,使用oldValue指向
E oldValue = get(elements, index);
//如果旧元素不等于新元素,复制一个一模一样的数组,替换下标元素,写入array
if (oldValue != element) {
//获取原数组长度,用于复制方法的参数
int len = elements.length;
//通过Arrays.copyof()方法复制一个新数组
Object[] newElements = Arrays.copyOf(elements, len);
//替换下标元素
newElements[index] = element;
//写入array,覆盖原数组
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
//旧元素=新元素,即没发生变化时,仍写入array
setArray(elements);
}
//返回旧元素
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
//add方法用于在尾部添加新元素:先复制一个新数组,在新数组中添加新元素,最后替换原数组
public boolean add(E e) {
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//获取原数组
Object[] elements = getArray();
//获取原数组长度,用于复制方法的参数
int len = elements.length;
//通过Arrays.copyof()方法复制一个新数组(因为要添加新元素所以新数组长度+1)
Object[] newElements = Arrays.copyOf(elements, len + 1);
//将新元素e放入新数组最后一位
newElements[len] = e;
//写入array,覆盖原数组
setArray(newElements);
//返回true
return true;
} finally {
//释放锁
lock.unlock();
}
}
//add方法的重载,还需要传入下标,根据下标添加新元素:先复制一个新数组,在新数组中给指定位置添加新元素,最后替换原数组
public void add(int index, E element) {
//获取ReentrantLock锁对象(不可改变)
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;
//如果为0,说明添加至数组尾部
if (numMoved == 0)
//复制一个与原数组一样的新数组,长度为原数组+1
newElements = Arrays.copyOf(elements, len + 1);
else {
//否则创建一个原数组长度+1的新数组
newElements = new Object[len + 1];
//复制原数组0到index-1的元素至新数组
System.arraycopy(elements, 0, newElements, 0, index);
//复制原数组index+1到尾部的元素至新数组
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//将新元素添加至指定位置
newElements[index] = element;
//写入array,覆盖原数组
setArray(newElements);
} finally {
//释放锁
lock.unlock();
}
}
//remove方法,根据下标删除元素:复制新数组,但不复制需要删除的元素,最后覆盖原数组
public E remove(int index) {
//获取ReentrantLock锁对象(不可改变)
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;
//如果长度为0,则说明要删的时最后一个元素
if (numMoved == 0)
//复制一个与原数组一样的新数组,长度为原数组-1,抛弃最后一个元素
setArray(Arrays.copyOf(elements, len - 1));
else {
//如果不是最后一个元素,创建一个新数组,长度为原数组-1
Object[] newElements = new Object[len - 1];
//将原数组中0到index-1的元素复制至新数组
System.arraycopy(elements, 0, newElements, 0, index);
//将原数组中index+1到末尾的元素复制至新数组
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
//写入array,覆盖原数组
setArray(newElements);
}
//返回
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
//removeRange方法,删除指定范围内的元素:复制新数组,不复制需要删除的范围内的元素,最后覆盖原数组
void removeRange(int fromIndex, int toIndex) {
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//获得原数组
Object[] elements = getArray();
//获得原数组长度
int len = elements.length;
//如果指定范围超出范围
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
//抛出索引越界异常
throw new IndexOutOfBoundsException();
//新数组长度为原数组长度-指定范围长度
int newlen = len - (toIndex - fromIndex);
//计算开始范围至数组尾部的长度
int numMoved = len - toIndex;
//如果至末尾长度为0,说明toIndex为最后一个元素
if (numMoved == 0)
//复制原数组的元素至新数组,长度为newlen
setArray(Arrays.copyOf(elements, newlen));
else {
//toIndex不是最后一个元素,则创建一个新的数组,长度为newlen
Object[] newElements = new Object[newlen];
//复制原数组0到formIndex范围内的元素至新数组
System.arraycopy(elements, 0, newElements, 0, fromIndex);
//复制原数组toIndex到末尾范围内的元素至新数组
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
//覆盖原数组
setArray(newElements);
}
} finally {
//释放锁
lock.unlock();
}
}
//removeAll方法,删除指定集合中的所有元素:复制新数组,不复制需要删除的范围内的元素,最后覆盖原数组
public boolean removeAll(Collection<?> c) {
//如果集合为空,抛出空指针异常
if (c == null) throw new NullPointerException();
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//获得原数组
Object[] elements = getArray();
//获得原数组长度
int len = elements.length;
//如果原数组长度不为0
if (len != 0) {
// temp array holds those elements we know we want to keep
//新数组长度
int newlen = 0;
//创建一个空数组长度为len
Object[] temp = new Object[len];
//遍历原数组
for (int i = 0; i < len; ++i) {
//获取每一个元素
Object element = elements[i];
//如果该元素不在指定集合内
if (!c.contains(element))
//添加至新数组
temp[newlen++] = element;
}
//新数组长度是否等于原数组
if (newlen != len) {
//不等于,说明删除成功覆盖
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
//等于,说明没删元素,返回false
return false;
} finally {
//释放锁
lock.unlock();
}
}
//清空数组
public void clear() {
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//创建新数组覆盖原数组
setArray(new Object[0]);
} finally {
//释放锁
lock.unlock();
}
}
// 添加批量元素
public boolean addAll(Collection<? extends E> c) {
// 将需要加入的集合转换为数组
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ? ((CopyOnWriteArrayList<?>) c).getArray()
: c.toArray();
// 如果长度为0,直接返回false
if (cs.length == 0)
return false;
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
//获得原数组
Object[] elements = getArray();
//获得原数组长度
int len = elements.length;
// 如果原数组为空并且类型为Object,则直接将新数组覆盖给原数组
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
// 原数组不为空,则创建一个新数组,将原数组的内容拷贝给新数组,长度为原数组长度加需要添加的元素数组的长度
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
// 将需要添加的元素拷贝给新数组
System.arraycopy(cs, 0, newElements, len, cs.length);
// 覆盖原数组
setArray(newElements);
}
return true;
} finally {
//释放锁
lock.unlock();
}
}
// 对集合内的元素进行排序,需要竞争锁 实现Comparator接口,定义排序规则
public void sort(Comparator<? super E> c) {
//获取ReentrantLock锁对象(不可改变)
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
// 获取原数组
Object[] elements = getArray();
// 拷贝原数组的内容到一个新数组
Object[] newElements = Arrays.copyOf(elements, elements.length);
//忽略警告
@SuppressWarnings("unchecked")
// 进行排序操作
E[] es = (E[]) newElements;
Arrays.sort(es, c);
// 覆盖原数组
setArray(newElements);
} finally {
//释放锁
lock.unlock();
}
}
// 比较指定对象与此列表的相等性
public boolean equals(Object o) {
// 先比较内存地址 如果相等直接返回true
if (o == this)
return true;
// 判断该对象的类型是否属于List,否则直接返回false
if (!(o instanceof List))
return false;
// 将该对象向上转型为List
List<?> list = (List<?>) (o);
// 获取list的迭代器
Iterator<?> it = list.iterator();
// 获取原数组
Object[] elements = getArray();
int len = elements.length;
// 遍历原数组 判断元素是否相等,不相等或者该对象元素不够则直接返回false
for (int i = 0; i < len; ++i)
if (!it.hasNext() || !eq(elements[i], it.next()))
return false;
// 遍历结束后该对象仍然有元素 直接返回false
if (it.hasNext())
return false;
return true;
}
// 获取该集合的哈希值
public int hashCode() {
//定义hash值为1
int hashCode = 1;
//获得原数组
Object[] elements = getArray();
//获得原数组长度
int len = elements.length;
//遍历原数组内元素
for (int i = 0; i < len; ++i) {
Object obj = elements[i];
// 通过算法获取该集合的哈希值
hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
}
//返回计算的hash值
return hashCode;
}
}