CopyOnWriteArrayList 源码分析-删除

176 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情

指定索引位置删除

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();
    }
}

lock.lock(); 首先进行加锁

E oldValue = get(elements, index); 取得即将删除的旧数据

if (numMoved == 0) setArray(Arrays.copyOf(elements, len - 1)); 判断要删除的数据是否在数组的尾部,如果是则直接删除

Object[] newElements = new Object[len - 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); 如果要删除的数据不在数组的尾部,则首先设置新数组的长度为原数组长度-1,然后从索引0位置开始,拷贝数组元素到尾部截止。

方法总结:

  1. 首先进行加锁
  2. 判断要删除数据的索引位置,然后选择不同的方式进行删除
  3. 最后进行解锁
  4. 整个方法使用try-catch进行包裹,能保证就算出现异常,锁也一定会被释放。

批量删除

源码:

public boolean removeAll(Collection<?> c) {
    if (c == null) throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        if (len != 0) {
            int newlen = 0;
            Object[] temp = new Object[len];
            for (int i = 0; i < len; ++i) {
                Object element = elements[i];
                if (!c.contains(element))
                    temp[newlen++] = element;
            }
            if (newlen != len) {
                setArray(Arrays.copyOf(temp, newlen));
                return true;
            }
        }
        return false;
    } finally {
        lock.unlock();
    }
}

if (c == null) throw new NullPointerException(); 首先对要删除额元素集合进行空判断

lock.lock(); 加锁

if (len != 0) { } 判断当前数组是否有值,如果当前数组不存在值的话则直接返回

int newlen = 0; 新数组的索引位置

for (int i = 0; i < len; ++i) { } 循环判断,将不存在于删除元素集合中的数据,放到新数组中

if (!c.contains(element)) temp[newlen++] = element;判断每个元素是否存在于删除元素集合中

if (newlen != len) { setArray(Arrays.copyOf(temp, newlen)); return true; } 拷贝新数组,变相的删除了不包含在 c 中的元素

方法总结: 集合元素批量删除并不会逐次调用指定元素删除的方法,而是先对原数组进行循环判断,把不需要删除的元素放到一个新数组,最后得到新数组就是结果。变相的达到了删除元素的目的。