当我们在当前节点之后添加一些对象或在当前节点之后删除一些对象时如何Iterator
抛出。ConcurrentModificationException
是否Iterator
维护对基础集合的副本或引用?
5 回答
迭代器维护对基础集合的引用。如果您添加或删除一个元素,迭代器可能会留在一个不可能的索引处,或者集合可能会从迭代器“从下面出来”改变。
因此,大多数集合不会在不通知您的情况下让迭代器损坏,而是在您尝试在迭代时修改集合时抛出 ConcurrentModificationException ,这样您就不会遇到不可预测的损坏迭代器。
根据合同,您不允许在迭代集合时修改集合(使用Iterator.remove()
et al 除外)。
当您这样做时,集合不会随机失败,而是足以跟踪它被修改的次数,并ConcurrentModificationException
在检测到并发修改时抛出。
ConcurrentModificationException 可能是你的朋友,你应该学会忍受它。但是,为了完整起见:
有一些非 Oracle 集合不会抛出 ConcurrentModificationException。它们更快(因为它们不花时间检查)并且显然更灵活,但在使用时需要更加小心。
Oracle 有四个(最后计数的)“并发”类,它们也不会在 java.util.concurrent 中抛出它(ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListMap 和 ConcurrentSkipListSet)。它们比它们的非并发等价物稍微慢一些,但它们是线程安全的并且它们不会阻塞。无论您做什么,它们都不会扰乱您的数据,但它们不会阻止您对其进行扰乱。
对于删除,您可以使用 iterator.remove(),如下所示:
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Object object = iterator.next();
/* ... */
if (condition) {
iterator.remove();
}
对于添加,您可以将简单的迭代器替换为 ListIterator,如下所示
ListIterator<Object> iterator = list.listIterator();
iterator.add(new Object());
当然,迭代器有一个指向底层集合的链接,这样可以避免复制。例如,如果您查看 ArrayList 迭代器 (ListItr) 的源代码,您会发现它主要包含指向列表的链接和光标。
所以,不要在线程之间共享迭代器,也不要修改你正在迭代的集合。