前言
Java中的CopyOnWriteArrayList是List接口的线程安全实现。它属于java.util.concurrent包,是ArrayList实现的增强版本。
顾名思义,CopyOnWriteArrayList为每个add()或set()操作创建基于ArrayList的克隆内部副本。由于此额外的开销成本,理想情况下,仅当我们具有非常频繁的读取操作并且没有很多插入或更新时,才应使用CopyOnWriteArrayList。
构建CopyOnWriteArrayList
我们可以使用以下构造函数之一创建一个CopyOnWriteArrayList:
- CopyOnWriteArrayList():创建一个空列表
- CopyOnWriteArrayList(Collection c):创建一个使用c中所有元素初始化的列表
- CopyOnWriteArrayList(Object [] obj):创建一个包含以下内容的列表:给定数组obj的副本让我们看几个示例:
示例代码如下:
CopyOnWriteArrayList<String> emptyList = new CopyOnWriteArrayList<>(); //[] list.add("A"); CopyOnWriteArrayList<String> listUsingCollection = new CopyOnWriteArrayList<>(list); //["A"] CopyOnWriteArrayList<String> listUsingArr = new CopyOnWriteArrayList<>(new String[] {"A", "B"}); //["A", "B"]
就像ArrayList一样,它为List接口的所有方法提供实现。
在CopyOnWriteArrayList上进行插入和迭代
众所周知,ArrayList的迭代器是快速失败的,即一旦实例化迭代器,当它在列表中检测到任何修改时,它将抛出ConcurrentModificationException。这也是很多
小伙伴初学者,在开发过程中,经常遇到的异常之一。
CopyOnWriteArrayList具有故障保护迭代器,即使我们在迭代过程中修改列表也不会引发异常。这是因为迭代器正在原始列表的克隆内部副本上进行操作。
但是,稍后实例化的迭代器将看不到对该列表所做的任何修改。
假设我们有一个CopyOnWriteArrayList实例,该实例当前包含两个元素:
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(new String[] {"A", "B"});
现在让我们获得一个Iterator实例,以便能够对其元素进行迭代:
Iterator itr = list.iterator();
创建完Iterator实例后,现在将更多元素添加到列表中:
list.add("C"); //此处将不会抛出上述异常
现在,让我们使用之前创建的itr遍历列表中的元素:
while(itr.hasNext()) { System.out.print(itr.next() + " "); }
打印结果如下所示:
A B
如我们所讲,它不会反映列表的当前状态,而只会打印“ A”和“ B”作为其元素。
通过CopyOnWriteArrayList进行删除和迭代
CopyOnWriteArrayList的迭代器不支持remove()操作。任何尝试这样做都会导致UnsupportedOperationException,很多同学在使用时,没有注意到此异
常,面试该类,也是常常会被提及的问题。
@Test(expected = UnsupportedOperationException.class) public void iterationAndRemoval() { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(new String[]{"A", "B"}); Iterator<String> itr = list.iterator(); while (itr.hasNext()) { itr.remove(); } }
ArrayList vs CopyOnWriteArrayList
让我们梳理一下Java ArrayList和CopyOnWriteArrayList类之间的区别:
ArrayList | CopyOnWriteArrayList |
属于java.util包,非线程安全的 | 是线程安全的实现,并且存在于java.util.concurrent包中 |
具有快速失败的迭代器,一旦迭代开始,它会在检测到任何修改时引发ConcurrentModificationException | 有一个故障安全的迭代器,因为该迭代器保存列表的克隆副本 |
迭代器支持remove()操作 | 迭代器不支持remove()操作,抛出UnsupportedOperationException |
总结
了解区别与联系,才能更好地使用,基于以上的区别,相信大家对于使用应该会有更明确清晰的认识。