概述:CopyOnWriteArrayList是实现了List接口的线程安全类:它在我们进行写操作(增、删、改)首先都加了ReentrantLock锁,其次在进行写操作的时候会将原数组拷贝一份生成新数组,在新数组上进行修改,最后让原数组指向新数组。在源码中能够看见它对读操作并未加锁,这可能在多个线程同时读会读到旧的值,为了更加的了解它的内部是如何,下面我们对核心源码部分进行查看。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//ReentrantLock锁(唯一)
final transient ReentrantLock lock = new ReentrantLock();
//CopyOnWriteArrayList用于存储数据的数组(Object[])
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
//作用:返回CopyOnWriteArrayList用于存储数据的数组(Object[] array)
final void setArray(Object[] a) {
array = a;
}
//作用:为数组设置值
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
//作用:初始化当前数组为一个空数组[]
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* @param a
* @param index
* 作用:返回Object[]中指定下标(index)的元素
* 直接按下标在数组中取值并返回
*/
public E get(int index) {
return get(getArray(), index);
}
/**
* 作用:返回数组中指定下标位置的元素
* 通过调用它上面重载的方法get(Object[] a, int 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();
}
}
/**
* 作用:设置指定下标位置元素的值
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、获取当前类中的Object[],并获取指定下标元素的原始值
* 3、判断原始值和设置的值是否相等
* 相等,则不做改变再次把数组set回去
* 不等,1)获取数组长度,并将整个Object[]赋值一份产生一个新数组
* 2)将新数组的指定下标改为指定值
* 3)让原始数组指向新数组
*/
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();
}
}
/**
* 作用:向数组中添加元素
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、获取当前Object[]和数组长度len
* 3、复制当前数组产生长度为len+1的新数组
* 4、让新数组的末尾元素【下标为len】值为添加的元素
* 5、让原始数组指向新数组完成添加操作并返回true
*/
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();
}
}
/**
* 作用:向集合中的指定位置添加元素
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、获取当前Object[]和数组长度len
* 3、判断传入的下标是否合法(0<=index<len),不合法抛出IndexOutOfBoundsException
* 4、定义新数组,计算需要移动的元素个数
* 5、若需移动的元素个数为0(即末尾添加指定元素),将原数组复制len+1个长度赋值给新数组
* 6、若需移动数!=0【即不在末尾】
* 6.1)新数组初始化长度为len+1
* 6.2)复制指定位置的前半部分【从0开始】到新数组【0-index】
* 6.3)复制后半部分(从index开始)到新数组【index+1,需移动元素数】空出index位置
* 7、为新数组的index位置附上要添加的元素值
* 8、让原数组指向新数组完成添加操作
*/
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();
}
}
/**
* 作用:移除集合中指定下标位置的元素
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、获取当前Object[]和数组长度len
* 3、获取指定下标元素的原始值并存储
* 4、获取需移动的元素数并判断
* 4.1)若为0,即删除末尾元素,复制原数组的len-1个长度并赋值给原数组
* 4.2)不为0,复制前半部分(从0开始)到新数组(0-index)
* 复制后半部分(从index+1开始)【跳过index位置】到新数组(index,移动数)
* 5、设置原数组的值为新数组完成删除操作
*/
void removeRange(int fromIndex, int toIndex) {
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;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, newlen));
else {
Object[] newElements = new Object[newlen];
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
/**
* 作用:移除集合中指定下标范围内的元素
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、获取当前Object[]和数组长度len
* 3、判断传入的下标是否合法(0,len),不合法抛出IndexOutOfBoundsException
* 4、计算数组的新长度newLen(移除后)、需移动的元素个数
* 5、若移动数为0(即从fromIndex到末尾全部移除),则复制原数组(从0开始)newLen个长度到新数组
* 若移动数不为0,复制前半部分(从0开始)到新数组(0)【fromIndex个长度】
* 复制后半部分(从toIndex开始)到新数组(从fromIndex)【长度为需移动的个数】
* 6、设置原数组的值为新数组
*/
public boolean removeAll(Collection<?> c) {
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];
if (!c.contains(element))
temp[newlen++] = element;
}
if (newlen != len) {
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
/**
* 作用:移除该集合中 含有的指定集合的元素(补集)
* 步骤:1、传入的集合为null,抛出NullPointerException
* 2、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 3、获取当前Object[]和数组长度len
* 4、判断长度:
* 4.1)若不为0,定义一个新长度newLen为0(累加,保存最后所剩的元素个数)
* 4.1.1)创建一个原数组长度len的Object[]
* 4.1.2)遍历原Object[],取出每一个元素判断是否包含于集合c中:
* 不包含则将该元素存入创建的新数组中,同时newLen++
* 4.1.3)如果新数组长度和原数组长度不等,则需要复制新数组的newLen个元素赋值给原数组,同时返回true
* 4.2)若为0,返回false
*/
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
setArray(new Object[0]);
} finally {
lock.unlock();
}
}
/**
* 作用:清空集合中的内容为[]
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、创建一个长度为0的Object[]赋值给原数组
*/
public boolean addAll(Collection<? extends E> c) {
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
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();
}
}
/**
* 作用:向原数组中添加指定集合中的元素
* 参数:cs数组
* 步骤:1、创建一个Object[]cs,判断传入集合类型和当前集合的类型是否相同:
* 1.1)相同,则直接初始化为传入集合的数组
* 1.2)不同,则将传入集合转换为数组完成cs的初始化
* 2、判断cs的长度,长度为0返回false
* 3、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 4、获取当前Object[]和数组长度len
* 5、判断是否 原数组长度为0 并且 cs的Class对象和Object[]的class对象一致
* 5.1)是,将cs数组直接赋值给原数组
* 5.2)不是,创建创建新数组(长度为原数组长度+集合的长度),同时完成整个原数组的复制
* 接着将集合中的元素(从0开始)复制到新数组中(从len开始)【长度为集合的长度】
* 设置原数组的值为新数组
* 6、返回true
*/
public void sort(Comparator<? super E> c) {
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();
}
}
/**
* 作用:对数组按照一定比较规则进行排序
* 参数:传入Comparator接口【它的实现类重写compare()定义了比较规则】
* 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
* 2、创建Object[]获取当前数组对象
* 3、创建新Objectp[]newElements初始化为当前数组的备份
* 4、在创建一个新Object[]cs指向newElements
* 5、通过Arrays工具类的静态方法sort传入cs和比较器Comparator对cs数组进行原地排序
* 6、设置当前数组为newElements
*/
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
List<?> list = (List<?>)(o);
Iterator<?> it = list.iterator();
Object[] elements = getArray();
int len = elements.length;
for (int i = 0; i < len; ++i)
if (!it.hasNext() || !eq(elements[i], it.next()))
return false;
if (it.hasNext())
return false;
return true;
}
/**
* 作用:用于比较对象
* 步骤:1、比较当前对象和传入对象的内存地址,相同返回true
* 2、判断当前对象是否为一个List类型,不是返回false
* 3、将传入的Object对象转换为List类型,并获取List集合的迭代器iterator()
* 4、获取当前Object[]和数组长度len
* 5、遍历Object[],同时List集合通过迭代器遍历
* 比较每一个元素,如果迭代器中没有下一个元素 || Object[]和List的第i个元素内容不相等返回false
* 获取原数组遍历结束,但List中还有元素返回false
* 否则返回true
*/
public int hashCode() {
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());
}
return hashCode;
}
/**
* 作用:获取对象的哈希值
* 步骤:1、定义hashCode变量并初始化为1
* 2、获取当前Object[]和数组长度
* 3、遍历当前的Object[],取出每一个元素
* 元素不为null,hashCode每次为原hashCode的31倍并加上当前元素的哈希值
* 4、循环结束,返回哈希值
*/