写时复制的集合,所以 CopyOnWriteArrayList 也是线程安全的,由于 CopyOnWriteArrayList 的存在, Vector 便慢慢不再使用
适用:读多写少的场景,如:黑白名单
缺点:1、浪费内存,2、弱一致性
在进行写操作的时候,会把 List 复制一份做写操作,操作完成后,在赋值回去,写的时候会加锁,但是不会阻塞读操作,适合读多写少的场景
public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8673264195747942595L;/** The lock protecting all mutators */final transient ReentrantLock lock = new ReentrantLock();// ...}
添加
首先上锁,获取原来的数组集合,然后进行复制拷贝,得到新的数组,然后对新的数组的最后一位进行赋值,完成添加操作后,将新的数组在设置回去,最后解锁
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();}}
按索引添加
首先上锁,先计算出前段要移动的数量,如果是 0 的话,表明就是添加到最后一个,那么就和 add 相同(复制整个数组),如果不是 0 的话,就复制一个空数组,长度比原来的多 1,然后拷贝前端数组(0~index)的位置,再拷贝后段数组(index~numMoved),此时新数组的 index 位会是空值,此时插入新值,完成赋值,解锁
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();}}
添加如果不存在
public boolean addIfAbsent(E e) {Object[] snapshot = getArray();return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :addIfAbsent(e, snapshot);}
