- List<String> list=new ArrayList<>();
- list.add("a");
- list.add("b");
- list.add("c");
- list.add("d");
- for (String s:list) {
- if (s.equals("d")) {
- list.remove(s);
- }
- }
- System.out.println(list);
做一个遍历的操作, 如果 list 中的某个元素满足某个条件, 则将该元素从 list 中移除
经过测试运行, 可以发现, 除非说要移除的元素位于倒数第二的元素, 否则会出现异常, 报错如下:
- Exception in thread "main" java.util.ConcurrentModificationException
- at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
- at java.util.ArrayList$Itr.next(Unknown Source)
- at Main.main(Main.java:49)
再看如下一例子
- List<String> list=new ArrayList<>();
- list.add("a");
- list.add("b");
- list.add("c");
- list.add("c");
- for (String s:list) {
- if (s.equals("c")) {
- list.remove(s);
- }
- }
- System.out.println(list);
期望得到的 list 应该只包含 a,b 两个元素, 然而得到的是如下的 list:
[a, b, c]
解决办法:
定义一个新的 list, 将要删除的 list 放在这个新定义的 list 中, 最后调用 removeAll 的方法整体删除
- List<String> dele=new ArrayList<>();
- List<String> list=new ArrayList<>();
- list.add("a");
- list.add("b");
- list.add("c");
- list.add("c");
- for (String s:list) {
- if (s.equals("c")) {
- dele.add(s);
- }
- }
- list.removeAll(dele);
- System.out.println(list);
错误分析:
首先知道 foreach 循环是使用迭代器来实现的, 找到源码
- // 在 AbstractList 类中
- // 内部成员有:
- // protected transient int modCount = 0;
- // 注: 该 modCount 相当于集合的版本号, 集合内容每改变一次, 其值就 + 1
- // 观察下面内部类可以知道, 在 next()remove() 方法都会去判断集合版本号与迭代器版本号是否一致 checkForComodification();
- // 内部方法有:
- public Iterator<E> iterator() {
- return new Itr();
- }
- private class Itr implements Iterator<E> {
- int cursor = 0;
- int lastRet = -1;
- //*** 创建迭代器时就讲版本号给了迭代器
- int expectedModCount = modCount;
- public boolean hasNext() {
- return cursor != size();
- }
- public E next() {
- checkForComodification();
- try {
- E next = get(cursor);
- lastRet = cursor++;
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public void remove() {
- if (lastRet == -1)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- expectedModCount = modCount;// 迭代器进行删除时, 会改变 it 的版本
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
- // 检查迭代器的版本与集合的版本是否一致, 不同则抛出异常
- final void checkForComodification() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- }
- }
需要知道两点:
1.modCount, 每一次 list 改变都会进行使其 + 1
2.cusor, 光标, 新遍历元素时候 + 1
该案列中, list.size()=4, 遍历集合时创建集合迭代器, 此时有 expectedModCount = modCount=4, 迭代器的 cursor=0,
(1)cursor=0,it.next() 检查版本号一致, 遍历第一个元素, cursor=cursor+1=1, 打印, 然后 it.hasNext() 函数判断 cursor!=list.size(), 返回 true
(2)cursor=1,it.next() 检查版本号一致, 遍历第二个元素, cursor=cursor+1=2, 打印, 然后 it.hasNext() 函数判断 cursor!=list.size(), 返回 true
(3)cursor=2,it.next() 检查版本号一致, 遍历第三个元素, cursor=cursor+1=3, 进入 if 语句, 移除一个元素, 此时集合内容改变, 版本号
modCount++,
为
5. 然后 it.hasNext() 判断 cursor(2)!=size(4),true
(
3)cursor=3,it.next() 检查发现版本号不一致 expectedModCount! = modCount, 抛出并发修改异常
而我们所举的第二个例子可以看出, 若要删除的元素位置在倒数第二个中, 则遍历会提前结束, 原因:
(1)cursor=0,it.next() 检查版本号一致, 遍历第一个元素, cursor=cursor+1=1, 打印, 然后 it.hasNext() 函数判断 cursor!=list.size(), 返回 true
(2)cursor=1,it.next() 检查版本号一致, 遍历第二个元素, cursor=cursor+1=2, 打印, 然后 it.hasNext() 函数判断 cursor!=list.size(), 返回 true
(3)cursor=3,it.next() 检查版本号一致, 遍历到要删除的元素, cursor=cusor+1=3; 进入 if 语句, list.remove(), 此时, 版本号 modCount++, 且集合大小改变 list.size()=3, 然后, it.hasNext() 判断发现 cursor(3)==size(3), 返回 false, 迭代提前结束
因此不会爆出异常
来源: http://www.bubuko.com/infodetail-2541153.html