CopyOnWriteArrayList 顾名思义就是在写的时候复制,也就是写的时候复制一个新数组将数据添加到新数组中然后在替换原来到数组;那么这样看来是不是很熟悉,在数据库中也又相似的思想就是一致性非锁定读。那么下面一起看一下具体实现的源码
// 可重入锁 final transient ReentrantLock lock = new ReentrantLock(); //持有数据的数组,只能通过getArray,setArray访问或赋值 private transient volatile Object[] array;
List的一个基本操作就是向集合中添加数据,那么下面一起看看add方法的具体实现
public boolean add(E e) { final ReentrantLock lock = this.lock; //加锁 lock.lock(); try { //获取数组 Object[] elements = getArray(); int len = elements.length; //长度增加1复制 Object[] newElements = Arrays.copyOf(elements, len + 1); //添加元素 newElements[len] = e; //设置数组 setArray(newElements); return true; } finally { //释放锁 lock.unlock(); } }
上面看了添加元素,又添加自然就有删除和修改,下面在一起看一下 删除和修改的具体实现
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(); } }
更新就是将元素添加到指定对索引位置上
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; //更新当前list中的数组 setArray(newElements); } else { // setArray(elements); } return oldValue; } finally { //释放锁 lock.unlock(); } }
如果不存在就添加,这个功能在后续的CopyOnWriteArraySet中需要使用到
public boolean addIfAbsent(E e) { //获取数组 Object[] snapshot = getArray(); //如果存在就返回false,否则就添加 return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false : addIfAbsent(e, snapshot); } private boolean addIfAbsent(E e, Object[] snapshot) { //加锁 final ReentrantLock lock = this.lock; lock.lock(); try { //获取当前数组 Object[] current = getArray(); //获取当前数组长度 int len = current.length; //如果快照不等于当前数组 if (snapshot != current) { // 获取快照和当前数组长度的较小值 int common = Math.min(snapshot.length, len); //遍历数组 for (int i = 0; i < common; i++) //如果数组中已经存在该元素则返回false if (current[i] != snapshot[i] && eq(e, current[i])) return false; if (indexOf(e, current, common, len) >= 0) return false; } //如果快照等于当前数组,则数组扩展1位后复制 Object[] newElements = Arrays.copyOf(current, len + 1); //将当前元素添加到数组 newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
private E get(Object[] a, int index) { return (E) a[index]; } public E get(int index) { return get(getArray(), index); }
通过上面增,删,改,查的实现可以了解了CopyOnWriteArrayList的基本实现,那么迭代应该怎么处理呢?通过内部类实现了一个对数据快照进行迭代对迭代器,既然是快照自然也就不支持增加,删除,修改等可以影响快照数据的操作了
static final class COWIterator<E> implements ListIterator<E> { //数据快照 private final Object[] snapshot; // 游标 private int cursor; private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } public boolean hasNext() { return cursor < snapshot.length; } public boolean hasPrevious() { return cursor > 0; } //获取游标位置的元素,同时游标值自增 public E next() { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; } //获取当前游标前的一个元素,如果存在 public E previous() { if (! hasPrevious()) throw new NoSuchElementException(); return (E) snapshot[--cursor]; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor-1; } //不支持对快照的修改 public void remove() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } public void add(E e) { throw new UnsupportedOperationException(); }
CopyOnWriteArraySet就是在CopyOnWriteArrayList上包装了一下,代码很简单
public class CopyOnWriteArraySet<E> extends AbstractSet<E> implements java.io.Serializable { private static final long serialVersionUID = 5457747651344034263L; private final CopyOnWriteArrayList<E> al; public CopyOnWriteArraySet() { al = new CopyOnWriteArrayList<E>(); } public CopyOnWriteArraySet(Collection<? extends E> c) { if (c.getClass() == CopyOnWriteArraySet.class) { @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc = (CopyOnWriteArraySet<E>)c; al = new CopyOnWriteArrayList<E>(cc.al); } else { al = new CopyOnWriteArrayList<E>(); al.addAllAbsent(c); } } public int size() { return al.size(); } public boolean isEmpty() { return al.isEmpty(); } public boolean contains(Object o) { return al.contains(o); } public Object[] toArray() { return al.toArray(); } public <T> T[] toArray(T[] a) { return al.toArray(a); } public void clear() { al.clear(); } public boolean remove(Object o) { return al.remove(o); } public boolean add(E e) { return al.addIfAbsent(e); } public boolean containsAll(Collection<?> c) { return al.containsAll(c); } public boolean addAll(Collection<? extends E> c) { return al.addAllAbsent(c) > 0; } public boolean removeAll(Collection<?> c) { return al.removeAll(c); } public boolean retainAll(Collection<?> c) { return al.retainAll(c); } public Iterator<E> iterator() { return al.iterator(); } }