How to avoid “ConcurrentModificationException” while removing elements from `ArrayList` while iterating it?
我试图从
1 2 3 4 5 |
当然,当我在迭代
使用
1 2 3 4 5 6 7 8 | Iterator<String> iter = myArrayList.iterator(); while (iter.hasNext()) { String str = iter.next(); if (someCondition) iter.remove(); } |
作为对其他人答案的替代,我总是这样做:
1 2 3 4 5 6 7 | List<String> toRemove = new ArrayList<String>(); for (String str : myArrayList) { if (someCondition) { toRemove.add(str); } } myArrayList.removeAll(toRemove); |
这将避免直接处理迭代器,但需要另一个列表。不管什么原因,我总是喜欢这条路线。
Java 8用户可以做到这一点:EDCOX1 OR 7
1 2 | List<String> list = new ArrayList<>(Arrays.asList("a","b","c")); list.removeIf(e -> (someCondition)); |
它将删除列表中满足某些条件的元素
您必须使用迭代器的remove()方法,这意味着没有增强的for循环:
1 2 3 4 5 6 | for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) { iterator.next(); if (someCondition) { iterator.remove(); } } |
不,不,不!
在单线程任务中,您不需要使用迭代器,而且,CopyOnWriteArrayList(由于性能下降)。
解决方案要简单得多:尝试使用规范的for循环,而不是针对每个循环。
根据Java版权所有者(几年前Sun,现在Oracle)的每个循环指南,它使用迭代器来遍历集合,只是隐藏它,使代码看起来更好。但不幸的是,正如我们所看到的,它产生的问题比利润更多,否则这个话题就不会出现。
例如,在修改后的arraylist上输入下一个迭代时,此代码将导致java.util.ConcurrentModificationException:
1 2 3 4 5 6 7 8 9 | // process collection for (SomeClass currElement: testList) { SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); } } |
但是下面的代码工作得很好:
1 2 3 4 5 6 7 8 9 10 11 | // process collection for (int i = 0; i < testList.size(); i++) { SomeClass currElement = testList.get(i); SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); i--; //to avoid skipping of shifted element } } |
因此,尝试使用索引方法迭代集合,并避免对每个循环使用,因为它们不是等价的!对于每个循环,使用一些内部迭代器,检查集合修改并引发ConcurrentModificationException异常。要确认这一点,请在使用我发布的第一个示例时仔细查看打印的堆栈跟踪:
1 2 3 4 | Exception in thread"main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at TestFail.main(TestFail.java:43) |
对于多线程,使用相应的多任务方法(如synchronized关键字)。
当其他建议的解决方案起作用时,如果您真的希望使解决方案具有线程安全性,则应将arraylist替换为copyonwritearraylist。
1 2 3 4 5 6 7 8 9 10 11 | //List<String> s = new ArrayList<>(); //Will throw exception List<String> s = new CopyOnWriteArrayList<>(); s.add("B"); Iterator<String> it = s.iterator(); s.add("A"); //Below removes only"B" from List while (it.hasNext()) { s.remove(it.next()); } System.out.println(s); |
另一种方法是将您的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | List myArrayList = Collections.synchronizedList(new ArrayList()); //add your elements myArrayList.add(); myArrayList.add(); myArrayList.add(); synchronized(myArrayList) { Iterator i = myArrayList.iterator(); while (i.hasNext()){ Object object = i.next(); } } |
如果要在遍历期间修改列表,则需要使用
可以使用迭代器remove()函数从基础集合对象中移除该对象。但在这种情况下,您可以从列表中删除同一个对象,而不是任何其他对象。
从这里