今天在做牛客的社区项目时,因为自己用错变量而弹出了一个异常,以前面见过,就记录一下,还是学到了新东西,就是java.util.ConcurrentModificationException这个异常
是在增强for循环中发现的,在开发社区评论功能的时候有如下一段代码
List<Comment> replyList = commentService.findCommentsByEntity(
ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
//回复的VO列表
List<Map<String, Object>> replyVoList = new ArrayList<>();
if (replyList != null) {
for (Comment reply : replyList) {
Map<String, Object> replyVo = new HashMap<>();
//回复
replyVo.put("reply", reply);
//作者
replyVo.put("user", userService.findUserById(reply.getUserId()));
//回复的目标
User taget = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
replyVo.put("target", taget);
replyVoList.add(replyVo);
}
}
然后我自己在add(replyVo)时代码写错了,写成了
replyList.add(replyVo);
这下就报错了,出现了文章开头说的的异常,把出错的代码提炼出来如下所示:
List<Comment> replyList = commentService.findCommentsByEntity(
ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
for (Comment reply : replyList) {
Map<String, Object> replyVo = new HashMap<>();
//回复
replyVo.put("reply", reply);
//作者
replyVo.put("user", userService.findUserById(reply.getUserId()));
//回复的目标
User taget = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
replyVo.put("target", taget);
replyList.add(replyVo);
}
就是以上代码,在我遍历完一次后再接着遍历的时候就抛出来的异常;然后就去网搜了下,大概意思就是咱们的ArrayList会维护一个modcount,咱ArrayList中的迭代器也会维护一个自己的count,当发现这两个count不一样的时候就会抛出这个异常。
下面我们来看看源码,看看到低在哪儿抛的异常
首先看看ArrayList的源码:(只展示一下主要部分喽)
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected transient int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
}
可以看到咱们ArrayList的增加和删除方法都会对modcount进行修改,然后来看看迭代器的源码
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
可以看到迭代器在每次调用next方法的时候都会检查modCount和expectedModCount是否相等,不想等就会抛出异常了。
好,现在再来回顾一下刚刚错误的代码,增强for循环的底层是迭代器遍历,所以在对replyList的增强for循环中,当我们第一次执行到replyList.add(replyVo)的时候我们replyList的modCount就会++,但是这个时候迭代器中的expectedModCount还是原来的值,所以在进行增强for循环执行第二次的时候,会执行迭代器的next()方法,这个时候一检查就发现replyList迭代器的expectedModCount和replyList的modCount不相等,所以这个时候就会抛出异常了
解决方案的话可以参考这篇这篇博客java.util.ConcurrentModificationException详解 - 简书
|