目录
分析
首先
然后
最后
解决方案
扩充
首先我们来看错误案例
1
2
3
4
5
6
7
8
9
10
11
12@Test public void doSomething(){ ArrayList<String> list = new ArrayList<String>(); list.add("昨天"); list.add("今天"); list.add("明天"); for (String s : list){ if (s.equals("昨天")){ list.remove(s); } } }
这个将会报错
java.util.ConcurrentModificationException
分析
首先
调用了三次add,我们来看list中add的源码(不会的可以看我关于源码博文的list讲解),最终调用了modCount++。这个modCount变量就是为了记录操作list的次数,详细可以看源码。
1
2
3
4
5
6
7private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
然后
我们都知道增强for循环,相当于是迭代器的语法糖。ArrayList中迭代器的方法源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71public Iterator<E> iterator() { return new Itr(); } //Itr为内部类 private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; //赋值给expectedModCount Itr() {} public boolean hasNext() { return cursor != size; } @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]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
增强for循环相当于在while里调用了hasnext方法以及调用了next的方法,最后调用了外部的remove方法(注意,不是内部类itr的方法,此时expectedModCount为3。
调用remove方法时的源码,实质调用了fastRemove,此刻modCount又加1变为了4
1
2
3
4
5
6
7
8private void fastRemove(int index) { modCount++; 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 }
最后
还记得上边循环调用了next方法吗,我们来看一下next方法
1
2
3
4
5
6
7
8
9
10
11public 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]; }
里边有个checkForComodification()
1
2
3
4final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
而此刻modCount为4,expectedModCount为3,不相等,所以抛出异常。
解决方案
我们注意到,在Itr内部类中有个remove方法,其中有个代码让expectedModCount=modCount(代码注释标出)
1
2
3
4
5
6
7
8
9
10
11
12
13
14public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; //是这个代码 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
所以我们需要调用内部类里边的remove。标准代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14@Test public void doSomething(){ ArrayList<String> list = new ArrayList<String>(); list.add("昨天"); list.add("今天"); list.add("明天"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ if (iterator.next().equals("昨天")){ iterator.remove(); } } System.out.println(list); }
扩充
这种机制成为“fail-fast”机制,也就是只要出现一个异常,程序及时止损不再往下运行。这种机制一般仅用于检测bug。
OK,今天也是一知半解的一天,如果对你有帮助,赶紧点赞收藏关注。
最后
以上就是会撒娇衬衫最近收集整理的关于为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充的全部内容,更多相关为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充内容请搜索靠谱客的其他文章。
发表评论 取消回复