路上碾压了一只鸡,就想找人讨要赔偿。
看到一个小女孩!我:“儿子!这是你的鸡吗?”
孩子:“不!我的鸡没那么平。”?
ArrayList使用forEach遍历时,删除元素会出错吗?
答:其实不一定。如果删除的元素是倒数第二个,它不会报告错误,否则,它将报告一个错误ConcurrentmodificationException。(A,会报错,没毛病)
原因:例如
List<String> lists = new ArrayList<String>();lists.add("1");lists.add("2");lists.add("3");lists.add("4");
如果要删除一个等于“3”的元素,我们都知道ArrayList的底层是以数组的形式存储数据的。生成一个元素后,后面的元素必须向前移动,列表的大小减少1。此时,列表变成大小为3的["1 "、" 2 "、" 4"]。
使用forEach遍历时:
for(String s :lists){ if(s.equals("3")){ lists.remove(s); }} //这是一颗语法糖,编译后相当于:for(Iterator i = lists.iterator();i.hasNext();){ String s = (String)i.next(); if(s.equals("3")){ list.remove(s); }}
迭代器的hasNext()方法确定大小是否与当前下标游标相同。如果大小相同,则没有剩余的元素。
如果删除元素“3”,大小将变为3。此时遍历的下标光标正好是3,所以不会进行下一个循环,直接结束。此时,元素“4”还没有被遍历。
如果列表中的元素是[“1”、“2”、“3”、“4”和“5”],即3不再是倒数第二个元素怎么办?
此时,将进行下一个循环。首先判断i.hasNext(),当前下标光标不等于size。执行i.next(),试图取出下一个值“4”。此时,会报告一个错误。原因在i.next():
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();}
迭代器取下一个值时,会先判断modCount是否与expectedModCount相同。如果不同,它将报告一个错误。
这里,modCount是被删除元素的数量计数,expectedModCount是迭代器期望的被删除元素的数量。使用迭代器的remove()方法时,迭代器会调用ArrayList。This.remove (latest)在制作modCount++时删除元素,然后将modCount的值赋给expectedModCount,保证两者相同。
所以这里就能发现问题了。在forEach循环中,我们直接使用lists.remove ("3 ")的方法删除元素,导致expectedModCount和ModCount不一致。
因此,要在遍历过程中删除元素,应该使用Iterator的方法,而不是forEach遍历。
下面是修改后的代码:
String s= null;for(Iterator i = lists.iterator(); i.hasNext(); ){ s=(String)i.next(); if(s.equals("3")){ i.remove(); }}
另一种方法是使用CopyOnWriteArrayList,而不是ArrayList,ArrayList是一个写时***容器。每次添加或删除一个元素,都会***一个旧数据,创建一个新数据,修改新数据后,旧数据的指针会修改为指向新数据。
这样遍历的数据实际上是第一个旧数据,旧数据是不变的。我们遍历旧数据,用新数据判断值。
画一张图来表达我的理解:
资料来源:blog.csdn.net/awocbb/article/details/85069427
本文来自玩味不尽投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/634524.html