在列表迭代期间从java.util.List中移除元素时,获取ConcurrentModificationExceptionexception?
@Test public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } for(String st:li){ if(st.equalsIgnoreCase("str3")) li.remove("str3"); } System.out.println(li); }
当我运行这个代码时,我将抛出ConcurrentModificationException。
它看起来好像当我从列表中删除指定的元素,列表不知道它的大小已经改变。
我想知道这是一个常见的问题收集和删除元素?
我相信这是Iterator.remove()方法背后的目的,能够在迭代时从集合中移除元素。
例如:
Iterator<String> iter = li.iterator(); while(iter.hasNext()){ if(iter.next().equalsIgnoreCase("str3")) iter.remove(); }
请注意,这个exception并不总是表明一个对象已经被不同的线程同时修改。 如果单个线程发出一系列违反对象约定的方法调用,则该对象可能会抛出此exception。 例如,如果一个线程使用快速迭代器迭代集合的时候直接修改了一个集合,迭代器将会抛出这个exception
取自http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html
将Java 8的方式从无List的Iterator中移除是:
li.removeIf(<predicate>)
即
List<String> li = new ArrayList<String>(); // ... li = li.removeIf(st -> !st.equalsIgnoreCase("str3"));
是的,人们碰到它 – 问题是你不能修改列表,而迭代它。 过去我已经使用了两种select:
- 您可以跟踪要删除的项目的索引,然后在迭代完成后将其删除。
- 或者,您可以在迭代时将所有想要保留的列表复制到新列表中,然后在完成时丢弃旧列表。
这些选项假设您必须迭代列表才能find要删除的元素 – 在列表元素是可以testing的属性的复杂对象的情况下很有用。
在你的特定情况下,你甚至不需要迭代,因为你可以使用removeAll。 看看这里的API。 还有一些漂亮的方法,比如retainAll,抛弃所有不在参数中的东西。 只要列表中的对象实现了equals和hashcode,就可以使用remove / retain-like方法。 如果你不能依靠equals / hashcode来确定你的应用中的实例之间的平等,你将不得不自己去除….
我得到了这个问题,我认为更简单的方法与hvgotcodes给出的第二种方法是一样的。
或者,您可以在迭代时将所有想要保留的列表复制到新列表中,然后在完成时丢弃旧列表。
@Test public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } List<String> finalLi = new ArrayList<String>(); for(String st:li){ if(st.equalsIgnoreCase("str3")){ // Do nothing } else { finalLi.add(st); } } System.out.println(finalLi); }
ArrayList具有字段modCount
– 收集修改的数量
当你调用方法时, iterator()
会创build新的对象Itr
。 它具有现场expectedModCount
。 expectedModCount
字段通过modCount
值进行初始化。 当你调用
li.remove("str3");
modCount
增量。 你什么时候通过迭代器检查expectedModCount == modCount
来尝试访问li
如果是false则抛出ConcurrentModificationException
因此,如果你得到的迭代器和修改后的集合 – 迭代器被认为是无效的,你不能使用它。
我觉得值得一提的是Java 8的版本
@Test public void testListCur() { List<String> li = new ArrayList<String>(); for (int i = 0; i < 10; i++) { li.add("str" + i); } li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList()); System.out.println(li); }
我以不同的方式循环…
public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } for(int i=0; i<li.size(); i++) if(li.get(i).equalsIgnoreCase("str3")) li.remove(i--); System.out.println(li); }
你可以创build一个你想从中删除元素的副本,直接在for-each循环中。 对我来说,这是最简单的方法。 像这样的东西:
for (String stringIter : new ArrayList<String>(myList)) { myList.remove(itemToRemove); }
希望能帮到你..
我认为最好的答案是来自bigdev.de,但我想添加一些东西(比如,如果项目从列表中删除,也许你想logging某处或某事):
List<String> list = new ArrayList<>(); list.removeIf(a -> { boolean condition = a.equalsIgnoreCase("some condition"); if(condition) logger.info("Item removed from the list: " + a); return condition; });
试试这个(Java 8):
list.removeIf(condition);