《java.util.concurrent 包源码阅读》08 CopyOnWriteArrayList和CopyOnWriteArraySet
CopyOnWriteArrayList和CopyOnWriteArraySet从数据结构类型上来说是类似的,都是用数组实现的保存一组数据的数据结构,区别也简单就是List和set的区别。因此这里就先讨论CopyOnWriteArrayList,然后再说CopyOnWriteArraySet。
这里重点关注的是CopyOnWrite,从字面上很容易理解,每当写操作的时候复制存储数据的数组,把拷贝上修改完再覆盖原先的数组。那么这样的数据结构适用的情况必然是读操作占绝大多数(很少进行写操作),且数据量不大的场合(例如黑名单)。
因此再来看CopyOnWriteArrayList的实现就很好理解了,必然会有一个volatile的数组,一把锁用于写操作(读操作的时候是不要锁的)。
/** The lock protecting all mutators */ transient final ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private volatile transient Object[] array;
复制整个数组使用Arrays.copyOf,部分复制数组使用System.arraycopy。下面以add方法为例:
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(); } }
可以看到如果在最后添加元素,则使用Arrays.copyOf新建并复制整个数组,如果在中间插入,则使用System.arraycopy分两段复制,然后插入元素。setArray方法的作用就是覆盖原来的数组,代码如下:
final void setArray(Object[] a) { array = a; }
下面来简单谈谈CopyOnWriteArraySet,其实CopyOnWriteArraySet就是一个CopyOnWriteArrayList的包装类:
private final CopyOnWriteArrayList<E> al;
区别在于因为Set不允许重复元素,因此CopyOnWriteArraySet的add方法调用的是CopyOnWriteArrayList的addIfAbsent方法
public boolean add(E e) { return al.addIfAbsent(e); }