用增强循环操作集合元素时通常都会抛出ConcurrentModificationException
普通for循环删除指定元素
1 | List<String> userNames = new ArrayList<String>() {{ |
1 | [hollis, HollisChuang, H] |
只删除了第一个元素,原因比较简单:删除了元素涉及元素移动,i++跳过了后面的元素
解决办法是倒着遍历或者使用迭代器遍历
增强for循环删除元素
1 | for(String name : userNames){ |
1 | Exception in thread "main" java.util.ConcurrentModificationException |
在增强for循环中使用add方法添加元素,结果也会同样抛出该异常
之所以会出现这个异常,是因为触发了一个Java集合的错误检测机制——fail-fast
对增强for循环语法糖进行解糖
1 | Iterator var2 = userNames.iterator(); |
反编译可看出其是由迭代器遍历实现
直接运行解糖的代码
1 | public E next() { |
可知next方法会校验这两个值,这两个值不相等时就会抛出并发修改异常
modCount是ArrayList中的一个成员变量。它表示该集合实际被修改的次数。调用集合的add和remove函数都会使该值加1
expectedModCount 是 ArrayList中的一个内部类——Itr中的成员变量。expectedModCount表示这个迭代器期望该集合被修改的次数。其值是在ArrayList.iterator方法被调用的时候初始化的。只有通过迭代器对集合进行操作,该值才会改变
上图中remove使用的是userNames.remove(name)集合的remove函数1
2
3
4
5
6
7
8
9
10
11
12
13
14public E remove(int index) {
rangeCheck(index);
modCount++;//+1
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;
}
集合的remove只操作了modCount值,expectedModCount并没有改变,所以在下一次next时就会不一致,抛出并发修改异常
解决办法
- 使用迭代器的remove函数
1
2
3
4
5
6
7Iterator iterator = userNames.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("Hollis")) {
iterator.remove();
}
}
1 | public void remove() { |
使用Java 8中提供的filter过滤
1
userNames = userNames.stream().filter(userName -> !userName.equals("Hollis")).collect(Collectors.toList());
使用增强for循环也可以,remove后直接退出,也就不会再次调用next方法
1
2
3
4
5
6for (String userName : userNames) {
if (userName.equals("Hollis")) {
userNames.remove(userName);
break;
}
}
使用迭代器怎么添加元素
Iterator迭代器没有添加功能,可使用其子接口ListIterator1
2
3
4
5
6
7
8
9
10ListIterator var2 = userNames.listIterator();
while(var2.hasNext()) {
String name = (String)var2.next();
if (name.equalsIgnoreCase("hollis")) {
var2.add("cccc");
}
}
System.out.println(userNames);
1 | [Hollis, cccc, hollis, cccc, HollisChuang, H] |