ArrayList循环遍历删除元素出现问题
- 1. 第一种循环删除出现异常
- 2. 第二种循环删除不报异常,但是会出现有些数据没有删除的情况
- 3. 总结
1. 第一种循环删除出现异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import java.util.ArrayList; /** * @author LanceQ * @version 1.0 * @time 2021/4/28 19:16 */ public class ListTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); for(String l:list){ System.out.println(list.remove(l));//并发修改异常 } } }
循环删除第二个开始爆并发异常
1
2
3true Exception in thread "main" java.util.ConcurrentModificationException
- 先了解一下remove方法,其方法有两个,一个是下面这个通过对象删除,一个是通过索引删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
- 一般情况下程序的执行路径会走到else路径下,最终调用faseRemove方法:
1
2
3
4
5
6
7
8
9
10
11
12private void fastRemove(int index) { modCount++; //AbstractList中的变量 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 }
- 其上面错误产生的原因:其实foreach写法是对实际的Iterable、hasNext、next方法的简写,问题在上文的fastRemove方法中,可以看到第一行把modCount变量的值加一,但在ArrayList返回的迭代器(该代码在其父类AbstractList中):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22protected transient int modCount = 0; public E next() { checkForComodification();<--------这里 try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } final void checkForComodification() { if (modCount != expectedModCount) <----判断 throw new ConcurrentModificationException();<--------这里异常 } int expectedModCount = modCount;
这里会做迭代器内部修改次数检查,因为上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。要避免这种情况的出现则在使用迭代器迭代时(显示或for-each的隐式)不要使用ArrayList的remove,改为用Iterator的remove即可。
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
34import java.util.ArrayList; import java.util.Iterator; /** * @author LanceQ * @version 1.0 * @time 2021/4/28 19:16 */ public class ListTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); //错误的删除方法 // for(String l:list){ // System.out.println(list.remove(l));//并发修改异常 // } System.out.println(list); Iterator<String> it = list.iterator(); //这里判断的是下一个元素是否存在 while (it.hasNext()){ System.out.println(it.hasNext()); //需要进行next操作才可以进行remove, // 否则会出现Java.lang.IllegalStateException异常(非法状态异常) it.next(); it.remove(); } System.out.println(list); } }
1
2
3
4
5
6
7运行结果 [1, 2, 3] true true true []
-
Java.lang.IllegalStateException异常(非法状态异常)出现的原因是 删除了一个不满足条件的元素。通过Iterator来删除,首先需要使用next方法迭代出集合中的元素,然后才能调用remove方法,否则集合可能抛出java.lang.IllegalStateException异常。
-
注意remove对象是否存在,如果这个记录已被remove掉,再次remove会出现此异常,容易出现在对同一对象(如List)做多次迭代remove的情景中。
2. 第二种循环删除不报异常,但是会出现有些数据没有删除的情况
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
26import java.util.ArrayList; import java.util.Iterator; /** * @author LanceQ * @version 1.0 * @time 2021/4/28 19:16 */ public class ListTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); remove2(list); } private static void remove2(ArrayList<String> list) { System.out.println(list); for (int i = 0; i < list.size(); i++) { System.out.println(list.remove(i)); } System.out.println(list); } }
1
2
3
4
5
6
7运行结果 [1, 2, 3, 4, 5] 1 3 5 [2, 4]
这次的remove调用的是另一个remove方法
1
2
3
4
5
6
7
8
9
10
11
12
13public 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; }
-
可以看到会执行System.arraycopy方法,导致删除元素时涉及到数组元素的移动。
-
针对上方出现的运行结果与预想不一致,是由于在遍历第一个字符串时,因为符合删除条件,所以将该元素从数组中删除,并且将后一个元素移动(也就是第二个字符串b)至当前位置,导致下一次循环遍历时,后一个字符串并没有遍历到,所以无法删除。
针对这种情况可以倒序删除的方式来避免:
1
2
3
4
5
6
7
8
9private static void remove2(ArrayList<String> list) { System.out.println(list); for (int i = list.size() - 1; i >= 0; i--) { System.out.println(list.remove(i)); } System.out.println(list); }
1
2
3
4
5
6
7
8[1, 2, 3, 4, 5] 5 4 3 2 1 []
3. 总结
-
通过foreach方式进行删除的modCount变量的改变,会出现非法状态异常,可通过iterator迭代器的方式进行判断,删除。
-
通过for循环变量list的长度,正序来进行list中元素的移除,会出现漏删除的情况,可通过倒序删除的方式来解决。
参考:https://www.cnblogs.com/huangjinyong/p/9455163.html
最后
以上就是愉快皮皮虾最近收集整理的关于ArrayList循环遍历并删除元素出现并发修改异常1. 第一种循环删除出现异常2. 第二种循环删除不报异常,但是会出现有些数据没有删除的情况3. 总结的全部内容,更多相关ArrayList循环遍历并删除元素出现并发修改异常1.内容请搜索靠谱客的其他文章。
发表评论 取消回复