迭代器生成器可迭代对象
在多线程以及单线程环境中都可能出现这种情况。
让我们通过以下示例探索这种情况:
import java.util.*;public class IteratorExample {public static void main(String args[]){List<String> myList = new ArrayList<String>();myList.add("1");myList.add("2");myList.add("3");myList.add("4");myList.add("5");Iterator<String> it = myList.iterator();while(it.hasNext()){String value = it.next();System.out.println("List Value:"+value);if(value.equals("3")) myList.remove(value);}Map<String,String> myMap = new HashMap<String,String>();myMap.put("1", "1");myMap.put("2", "2");myMap.put("3", "3");Iterator<String> it1 = myMap.keySet().iterator();while(it1.hasNext()){String key = it1.next();System.out.println("Map Value:"+myMap.get(key));if(key.equals("2")){myMap.put("1","4");//myMap.put("4", "4");}}}
}
输出为:
List Value:1
List Value:2
List Value:3
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)at java.util.AbstractList$Itr.next(AbstractList.java:343)at com.journaldev.java.IteratorExample.main(IteratorExample.java:27)
从输出堆栈跟踪中可以明显看出,当我们调用迭代器next()函数时,异常即将到来。 如果您想知道Iterator如何检查修改,则它的实现存在于AbstractList类中,其中定义了一个int变量modCount,该变量提供了更改列表大小的次数。 该值在每个next()调用中使用,以检查功能checkForComodification()中是否有任何修改。
现在,注释列表部分并再次运行该程序。
输出将是:
Map Value:3
Map Value:2
Map Value:4
由于我们正在更新myMap中的现有键值,因此其大小没有更改,并且没有收到ConcurrentModificationException。 请注意,输出结果可能在您的系统中有所不同,因为HashMap键集的排序不像list那样。 如果您将在HashMap中添加新键值的语句取消注释,则将导致ConcurrentModificationException。
要在多线程环境中避免ConcurrentModificationException:
1.您可以将列表转换为数组,然后在数组上进行迭代。 这种方法适用于中小型列表,但是如果列表很大,则对性能的影响很大。
2.您可以通过将列表放在同步块中来在锁定时锁定列表。 不建议使用此方法,因为它将停止多线程的好处。
3.如果使用的是JDK1.5或更高版本,则可以使用ConcurrentHashMap和CopyOnWriteArrayList类。 这是推荐的方法。
要在单线程环境中避免ConcurrentModificationException:
您可以使用迭代器remove()函数从基础集合对象中删除该对象。 但是在这种情况下,您可以从列表中删除同一对象,而不能删除任何其他对象。
让我们使用并发集合类运行示例:
package com.journaldev.java;import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;public class ThreadSafeIteratorExample {public static void main(String[] args) {List<String> myList = new CopyOnWriteArrayList<String>();myList.add("1");myList.add("2");myList.add("3");myList.add("4");myList.add("5");Iterator<String> it = myList.iterator();while(it.hasNext()){String value = it.next();System.out.println("List Value:"+value);if(value.equals("3")){myList.remove("4");myList.add("6");myList.add("7");}}System.out.println("List Size:"+myList.size());Map<String,String> myMap = new ConcurrentHashMap<String,String>();myMap.put("1", "1");myMap.put("2", "2");myMap.put("3", "3");Iterator<String> it1 = myMap.keySet().iterator();while(it1.hasNext()){String key = it1.next();System.out.println("Map Value:"+myMap.get(key));if(key.equals("1")){myMap.remove("3");myMap.put("4", "4");myMap.put("5", "5");}}System.out.println("Map Size:"+myMap.size());}}
输出为:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Size:6
Map Value:1
Map Value:null
Map Value:4
Map Value:2
Map Size:4
从上面的示例可以清楚地看出:
1.可以修改Concurrent Collection类,避免ConcurrentModificationException 。
2.对于CopyOnWriteArrayList ,迭代器不适应列表中的更改,并且可以处理原始列表。
3.对于ConcurrentHashMap ,行为并不总是相同的。
条件:
if(key.equals("1")){myMap.remove("3");
输出为:
Map Value:1
Map Value:null
Map Value:4
Map Value:2
Map Size:4
它正在使用添加了键“ 4”的新对象。 但不是下一个键为“ 5”的对象。
现在,如果我将条件更改为
if(key.equals("3")){myMap.remove("2");
输出为:
Map Value:1
Map Value:3
Map Value:null
Map Size:4
在这种情况下,它不考虑新添加的对象。
因此,如果您使用的是ConcurrentHashMap,请避免添加新对象,因为可以根据键集对其进行处理。 请注意,同一程序可以在您的系统中打印不同的值,因为HashMap键集没有任何顺序。
额外的浇头:
for(int i = 0; i<myList.size(); i++){System.out.println(myList.get(i));if(myList.get(i).equals("3")){myList.remove(i);i--;myList.add("6");}
}
如果您在单线程环境中工作,并且希望您的代码处理列表中额外添加的对象,则可以使用以下代码并避免使用迭代器。
请注意,由于要删除同一对象,所以要减少计数器,如果必须删除下一个或更远的对象,则不需要减少计数器。
自己尝试。
参考:在JournalDev上 使用 JCG合作伙伴提供的迭代器时如何避免ConcurrentModificationException 。
相关文章:
- Java最佳实践– Vector vs ArrayList vs HashSet
- Java最佳实践–队列之战和链接的ConcurrentHashMap
- Java Fork / Join进行并行编程
- ConcurrentLinkedHashMap v 1.0.1发布
- 阻塞队列示例以执行命令
- 信号量示例限制URL连接
- 执行命令的同步队列示例
- 更一般的等待/通知机制的CountDownLatch示例
翻译自: https://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html
迭代器生成器可迭代对象