CopyOnWriteArrayList特殊点, 对比ArrayList

CopyOnWriteArrayList特殊点, 对比ArrayList

  • ArrayList可以使用迭代器的remove删除元素, CopyOnWriteArrayList不能使用迭代器的remove方法删除, 只能使用集合的remove方法删除
public class Demo15 {
    public static void main(String[] args) {
        // 使用ArrayList的iterator删除元素
        List<String> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        list1.add("c");
        Iterator<String> iterator1 = list1.iterator();
        while (iterator1.hasNext()) {
            String str = iterator1.next();
            if ("b".equals(str)) {
                iterator1.remove();
            }
        }

        // 使用CopyOnWriteArrayList的iterator删除元素
        List<String> list2 = new CopyOnWriteArrayList<>();
        list2.add("a");
        list2.add("b");
        list2.add("c");
        Iterator<String> iterator2 = list2.iterator();
        while (iterator2.hasNext()) {
            String str = iterator2.next();
            if ("b".equals(str)) {
                iterator2.remove(); // 这里会抛UnsupportedOperationException异常
            }
        }
    }
}

分析:
CopyOnWriteArrayList源码:
查看CopyOnWriteArrayList获取迭代器方法

public Iterator<E> iterator() {
    return new CopyOnWriteArrayList.COWIterator(this.getArray(), 0);
}

查看内部类COWIterator构造方法

COWIterator(Object[] es, int initialCursor) {
    this.cursor = initialCursor;
    this.snapshot = es;
}

看到这里, 实际上迭代器用的是CopyOnWriteArrayList当前数组的快照引用, 后续迭代器的操作都是对这个快照引用的操作, 怎么理解快照引用呢? 注意CopyOnWriteArrayList内部的数组引用, 在集合增删的时候会变, 调用setArray对引用重新赋值, 那么迭代器里面的快照引用就变成了过时的, 非最新的, 如果迭代器的remove方法要想支持删除的话, 就不能对快照引用操作, 而应该对最新的引用操作, 所以集合的remove和迭代器的remove至少要做到加锁, 问题就很麻烦了, 简单点处理, 干脆迭代器的删除直接抛异常, 不支持, 然后就只能用集合的remove…

public void remove() {
    throw new UnsupportedOperationException();
}

而ArrayList的迭代器还是对原集合的数组直接操作, 但是每次删除元素的时候会检查修改次数是否一致, 由于实时比较ArrayList集合size和修改次数modCount, 一旦不一致, 就抛并发修改异常, 会导致无法删除

  • ArrayList增强for循环不能调用集合的remove删除元素, 而CopyOnWriteArrayList却可以
public class Demo15 {
    public static void main(String[] args) {
        // ArrayList增强for循环删除, 报错
        List<String> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        list1.add("c");
        for (String str : list1) {
            list1.remove(str);
        }

        // CopyOnWriteArrayList增强for循环删除, 正常
        List<String> list2 = new CopyOnWriteArrayList<>();
        list2.add("a");
        list2.add("b");
        list2.add("c");
        for (String str : list2) {
            list2.remove(str);
        }
    }
}

分析:
集合中增强for都转换为迭代器进行遍历, 关键就在于ArrayList的remove方法不能在迭代器遍历元素中调用, 而CopyOnWriteArrayList的remove方法却可以在迭代器遍历元素中调用

从设计结构上看:
ArrayList:
ArrayList每次获取的都是新的迭代器, 用当前ArrayList的修改次数初始化了迭代器, 迭代器内部维护着的修改次数是用来判断ArrayList是否有单独作修改(list.remove方法), 或者被其他迭代器修改(多线程下同个集合获取多个迭代器), 若再使用ArraList的增删等操作, 集合中修改次数更改了却无法同步到迭代器内部, 导致再次调用迭代器的next方法或者remove方法时, 会检查修改次数, 不一致则报错, 而迭代器的删除操作会同步到ArrayList中
CopyOnWriteArrayList:
CopyOnWriteArrayList每次获取的迭代器, 都是对当前集合中数组库引用的拷贝, 所以在增强for中, 只是对原先数组副本进行遍历, 而删除的操作, 会从集合中拷贝新的数组进行操作, 自然可以删除成功

应用场景:多线程下读多写少
使用CopyOnWriteArrayList主要解决多线程下, 读写线程可以并发执行问题, 结果如下:
1.读读线程可以并发执行
2.读写线程可以并发执行
3.写写线程不可以并发执行

存在问题点:
1.每次增删元素都要拷贝耗费资源大
2.读的数据是份快照, 并不是实时的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值