如果在迭代时修改集合而不抛出ConcurrentModificationException,会发生什么

时间:2014-04-17 16:22:45

标签: java data-structures iterator

众所周知,我们不能在迭代它时修改非线程安全的集合,因为它会抛出一个ConcurrentModificationException

但我想知道的是,如果它不会抛出异常并让迭代和修改同时发生会发生什么。

例如,在迭代时从HashMap中删除一个元素。

  1. 删除。由于删除操作不会改变HashMap中基础表的长度,我认为这不是迭代的问题。

  2. 把。也许问题只发生在Put触发器调整大小()时,因为基础表将被洗牌。

  3. 我的分析是否正确?

3 个答案:

答案 0 :(得分:5)

简答:不,你的分析不正确。

如果你在迭代它时(不使用迭代器)从集合中删除了某些东西,迭代器就没有很好的方法来跟踪它的位置。使用更简单的示例:List。假设迭代器位于索引10处,并删除索引5.该删除会移动所有索引。现在你在迭代器上调用next(),你......什么?转到索引11?保持在10指数?迭代器无法知道。

类似地,如果你在对它进行迭代时添加一些东西(不使用迭代器),迭代器也不知道它是在当前索引之前还是之后添加的,所以下一个()函数被破坏。

这甚至不会进入迭代器顺序取决于集合中的内容的数据结构,但问题类似于我上面列出的问题。

答案 1 :(得分:2)

  

但我想知道的是,如果它不会抛出异常并让迭代和修改同时发生会发生什么。

这是假设的,因为相应的(非并发)收藏品不会以这种方式工作。如果我们假设他们确实允许"并发"修改,那么我们仍然无法回答如何实现迭代的假设。最后,假设我们刚刚删除了快速失败测试,​​那么行为将是特定于集合的。

查看对HashMap案例的分析,您必须考虑迭代器对象的内部状态。我没有看过任何具体的实现代码,但是一个典型的HashMap迭代器将在主哈希数组中有一个哈希链的索引,以及一个指向哈希链中的节点的指针:

  • Map.remove不会改变散列图大小,因此链索引不会失效。但是,如果删除了错误的条目,我们可以发现迭代器的节点指针可以引用不再在链中的节点。这可能导致迭代返回已删除的地图条目。

  • 您是正确的,触发调整大小的Map.put可能导致重新分配条目。这可能会导致某些条目被跳过,而其他条目将被退回两次。

答案 2 :(得分:0)

通常java.util包中的传统集合类使用int变量(modCount)来跟踪修改(添加和删除)。

当我们从这些集合类中请求Iterator时,返回的Iterator对象将提供现有的修改计数变量作为其预期的修改计数。

在调用next()方法时,Iterator对象会根据其预期的修改计数值检查当前修改计数变量值。

如果不匹配,则ConcurrentModificationException包中存在java.util,其RuntimeExceptionvar configValues = getUserAgentValues(); var browserName = configValues.browser_name ; var browserVersion = configValues.browser_vers; var OSVersion = configValues.platform_type;

不要混淆集合对象的大小(如问题Map中所示)和可用的总桶数。此外,它与大小无关,一次添加会增加修改计数标志的值,并且删除也会增加其值。