CopyOnWriteArrayList(遍历中删除元素)

本文深入探讨了CopyOnWriteArrayList的实现原理,解释了如何在遍历过程中安全地删除元素,避免并发修改异常。通过源码分析,揭示了其线程安全机制及对象删除与索引删除的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CopyOnWriteArrayList

在之前的像素鸟游戏中我遇到一个问题,我想要在遍历List的同时根据一定条件删除List中的元素。

但是有一个问题,我们不能在遍历一个List的同时对于其内的元素进行删改的操作。

这时候会给出这样的错误:

java.util.ConcurrentModificationException

我不想使用迭代的方式完成这个功能,那样很麻烦,难道我不能使用List把这个问题解决么?

这个时候我了解了一个List的实现类CopyOnWriteArrayList。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWrite这几个单词很好理解,那么他是怎么实现在循环内完成对于遍历中数据的删除的呢?

其实不难猜测,应该是使用copy一个新的List进行增删改的操作,循环结束再进行复制。

让我们看一下源码

public boolean remove(Object o) {
    Object[] snapshot = getArray();
    int index = indexOf(o, snapshot, 0, snapshot.length);
    return (index < 0) ? false : remove(o, snapshot, index);
}

果不其然。

到这已经完成了我们的需求,但是看到这里不妨继续看看它还进行了怎样的封装。

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

可以看到除了我们使用的对于对象的删除CopyOnWriteArrayList还提供了索引删除的方式,并且进行了加锁处理,可以想见CopyOnWriteArrayList类是支持多线程操作的。

看到这里我有点儿小蒙,既然移除索引位置对象需要保证线程安全,那么为什么对于对象的移除却并没有呢?仔细看源码我发现再其中调用了另一个remove函数。

private boolean remove(Object o, Object[] snapshot, int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) findIndex: {
                int prefix = Math.min(index, len);
                for (int i = 0; i < prefix; i++) {
                    if (current[i] != snapshot[i] && eq(o, current[i])) {
                        index = i;
                        break findIndex;
                    }
                }
                if (index >= len)
                    return false;
                if (current[index] == o)
                    break findIndex;
                index = indexOf(o, current, index, len);
                if (index < 0)
                    return false;
            }
            Object[] newElements = new Object[len - 1];
            System.arraycopy(current, 0, newElements, 0, index);
            System.arraycopy(current, index + 1,
                             newElements, index,
                             len - index - 1);
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

在这个重载函数中才真正的进行了多线程处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值