写时复制的集合,所以 CopyOnWriteArrayList 也是线程安全的,由于 CopyOnWriteArrayList 的存在, Vector 便慢慢不再使用

适用:读多写少的场景,如:黑白名单
缺点:1、浪费内存,2、弱一致性

在进行写操作的时候,会把 List 复制一份做写操作,操作完成后,在赋值回去,写的时候会加锁,但是不会阻塞读操作,适合读多写少的场景

  1. public class CopyOnWriteArrayList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
  3. private static final long serialVersionUID = 8673264195747942595L;
  4. /** The lock protecting all mutators */
  5. final transient ReentrantLock lock = new ReentrantLock();
  6. // ...
  7. }

添加

首先上锁,获取原来的数组集合,然后进行复制拷贝,得到新的数组,然后对新的数组的最后一位进行赋值,完成添加操作后,将新的数组在设置回去,最后解锁

  1. public boolean add(E e) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. Object[] newElements = Arrays.copyOf(elements, len + 1);
  8. newElements[len] = e;
  9. setArray(newElements);
  10. return true;
  11. } finally {
  12. lock.unlock();
  13. }
  14. }

按索引添加

首先上锁,先计算出前段要移动的数量,如果是 0 的话,表明就是添加到最后一个,那么就和 add 相同(复制整个数组),如果不是 0 的话,就复制一个空数组,长度比原来的多 1,然后拷贝前端数组(0~index)的位置,再拷贝后段数组(index~numMoved),此时新数组的 index 位会是空值,此时插入新值,完成赋值,解锁

  1. public void add(int index, E element) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. if (index > len || index < 0)
  8. throw new IndexOutOfBoundsException("Index: "+index+
  9. ", Size: "+len);
  10. Object[] newElements;
  11. int numMoved = len - index;
  12. if (numMoved == 0)
  13. newElements = Arrays.copyOf(elements, len + 1);
  14. else {
  15. newElements = new Object[len + 1];
  16. System.arraycopy(elements, 0, newElements, 0, index);
  17. System.arraycopy(elements, index, newElements, index + 1,
  18. numMoved);
  19. }
  20. newElements[index] = element;
  21. setArray(newElements);
  22. } finally {
  23. lock.unlock();
  24. }
  25. }

添加如果不存在

  1. public boolean addIfAbsent(E e) {
  2. Object[] snapshot = getArray();
  3. return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
  4. addIfAbsent(e, snapshot);
  5. }