一)CopyOnWriteArrayList<E>简介
CopyOnWriteArrayList<E>源码详解(基于jdk1.8.0_162)
说明:CopyOnWriteArrayList<E>是ArrayList的变体,是一个线程安全的集合。
原理:每一次在操作集合的时候,都是先新创建一个数组,填充数据,再把新数组的数据复制一份到源数组中。
缺点:CopyOnWriteArrayList<E>其中所有可变操作(add,remove,set等等),比较耗性能。
优点:适用于遍历较多的场景。
备注:CopyOnWriteArraySet<E>类是在CopyOnWriteArrayList<E>基础上实现的。
二)构造方法
1)CopyOnWriteArrayList();
创建一个空列表。
2)CopyOnWriteArrayList(Collection<? extends E> c) ;
按照集合的迭代器返回的顺序创建一个包含指定集合元素的列表。
3)CopyOnWriteArrayList(E[] toCopyIn);
创建一个包含给定数组的副本的列表。
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
三)添加
1)boolean add(E e):一次添加一个元素。
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(); // 解锁
}
}
2)void 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; // 源数据大小
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); // 把源数据index之前的数据先复制到新数组
System.arraycopy(elements, index, newElements, index + 1,
numMoved); // 把源数据index之后的数据先复制到新数组
}
newElements[index] = element; // 插入元素
setArray(newElements); // 把新数组覆盖源数据
} finally {
lock.unlock(); // 解锁
}
}
四)查找
1)E get(int index):返回此列表中指定位置的元素。
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
2)int indexOf(Object o):返回此列表中指定元素的第一次出现的索引,从 index向前 index ,如果未找到该元素,则返回-1。
public int indexOf(Object o) {
Object[] elements = getArray(); // 获取源数据
return indexOf(o, elements, 0, elements.length); // 调用查找方法
}
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) { // 如果查找元素为null
for (int i = index; i < fence; i++)
if (elements[i] == null) // 判断元素是否等于null
return i;
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i])) // 判断元素是否相等
return i;
}
return -1;
}
五)修改
1)E set(int index, E element):用指定的元素替换此列表中指定位置的元素。
public E set(int index, E element) {
final ReentrantLock lock = this.lock; // 加锁
lock.lock();
try {
Object[] elements = getArray(); // 获取源数据
E oldValue = get(elements, index); // 获取源数据中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)E remove(int index):删除该列表中指定位置的元素。
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); // 获取源数据index位置的元素
int numMoved = len - index - 1; // 计算需要移动的位置
if (numMoved == 0) // 如果移动位置为0
setArray(Arrays.copyOf(elements, len - 1)); // 复制一个新数组覆盖源数据
else {
Object[] newElements = new Object[len - 1]; // 声明一个新数组
System.arraycopy(elements, 0, newElements, 0, index); // 把index之前的数据复制到新数组
System.arraycopy(elements, index + 1, newElements, index,
numMoved); // // 把index之后的数据复制到新数组
setArray(newElements); // 重新覆盖源数据
}
return oldValue;
} finally {
lock.unlock(); // 解锁
}
}
总结:CopyOnWriteArrayList<E>除了遍历和查找,没有创建新数组,其它新增、修改、删除等方法都会不断创建新数组,再覆盖源数据。所以CopyOnWriteArrayList<E>适用于遍历场景较多的情况下。
只列举了常用的几个方法,其它的非常用方法差不多。
识别二维码关注个人微信公众号
本章完结,待续,欢迎转载!
本文说明:该文章属于原创,如需转载,请标明文章转载来源!