记录下在工作中发生的一个异常,深入源码分析观察原因,以及在之后写代码的过程中如何避免。具体的报错如下:
1 | Exception in thread "main" java.util.ConcurrentModificationException |
ConcurrentModificationException产生原因
定位到最主要的一段代码,报错是在HashIterator中暴出来的。代码如下:
1 | final Node<K,V> nextNode() { |
其中modCount是HashMap中实际存在的元素节点数量,而expectedModCount是复制出来的迭代器中的节点数量。来看下我们产生报错的代码:
1 | package com.souche.JsonTest; |
通过jsonObject.remove()函数我们减少了modCount的值,但是expectedModCount的值没有变。那么迭代器在继续进行下一个元素迭代的时候会报错。值得注意的是,如果先通过jsonObject.put()新增一个元素,然后又通过jsonObject.remove移除一个元素,是不会报这个错的,因为这里判断仍然相等,但是实际上是被修改了的。我们可以看一下上面描诉的效果:
1 | package com.souche.JsonTest; |
正确遍历HashMap(Iterator/ConcurrentHashMap)
正常情况下我们获取到迭代器之后,需要通过迭代器去操作,防止发生上面我们介绍的异常。上面的代码改成迭代器的版本。
1 | public void usingIterator(JSONObject json){ |
我们通过迭代器去删除的时候进行expectedModCount–同时会执行modCount–操作,不会报错。当然如果我们使用ConcurrentHashMap也可以避免这个异常。
1 | public void usingConcurrentHashMap(JSONObject jsonObject){ |
通过ConcurrentHashMap不会发生ConcurrentModificationException异常。注意ConcurrentHashMap的value不允许为空值。