1、fail-fast与fail-safe:

     每次我们尝试在集合中获取下一个元素的时候,Iterator fail-fast属性检查当前集合结构里的任何改动。如果发现任何改动,它抛出ConcurrentModificationException异常。Collection中所有Iterator的实现都是按fail-fast来设计的。而java.util.concurrent中的集合类都为fail-safe的,不会抛出ConcurrentModificationException异常。

2、ConcurrentModificationException异常:

如果对正在被迭代的集合进行结构上的改变(即对该集合使用add、remove或clear方法),那么迭代器就不再合法(且在其后使用该迭代器将会有ConcurrentModificationException异常被抛出)

 

iterator和ConcurrentModificationException、java.lang.UnsupportedOperationException异常_多线程

原因是内部modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。解决方法:

1)单线程环境,使用iterator:

public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(2);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer==2)
iterator.remove(); //注意这个地方
}
}
}

注:.next()必须在.remove()之前调用。使用Iterator的remove方法时不会对集合是否被更改进行判断,所以上面不会出现ModifyException。

2)多线程环境:

上面方法在多线程环境下也会报ConcurrentModificationException异常,有朋友可能会说ArrayList是非线程安全的容器,换成Vector就没问题了,实际上换成Vector还是会出现这种错误。原因在于,虽然Vector的方法采用了synchronized进行了同步,但是由于Vector是继承的AbstarctList,因此通过Iterator来访问容器的话,事实上是不需要获取锁就可以访问。

为什么多线程会出现上述异常呢?

由于modCount是AbstarctList的成员变量,多线程来修改一个值,可能会造成某一个线程中expectedModCount和modCount不等,进而抛出异常。解决方法:

  • 在使用iterator迭代的时候使用synchronized或者Lock进行同步;
  • 使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

for (String str:list) {
System.out.println(str);
list.add("ddd");
list.remove("aaa");
}
System.out.println(list);

输出:
aaa
bbb
ccc
[bbb, ccc, ddd, ddd, ddd]

使用CopyOnWriteArrayList的remove方法时,该方法会先将List复制一份,对副本进行修改,然后把以前的List引用重新指向副本,所以不会出现异常。

注:但是对于CopyOnWriteArrayList,不能使用iterator进行remove。

3、Arrays.asList引起的惨案(UnsupportedOperationException异常):

public static void main(String[] args)
{
List<String> list = Arrays.asList("1", "2", "3", "4");
for (Iterator<String> iter = list.listIterator(); iter.hasNext();)
{
String a = iter.next();
if (true)
{
iter.remove();
}
}
}

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(Unknown Source)
at java.util.AbstractList$Itr.remove(Unknown Source)

反编译后才发现原来 Arrays.asList返回的ArrayList并不是java.util.ArrayList,而是在Arrays类中重新定义的一下内部类ArrayList,该类集成了AbstractList,并没有重写remove方法。所以,改成如下方式,就可使用iterator的remove方法了。

List list = new ArrayList(Arrays.asList("1",...));

上面的错误,在protobuf中也遇到过一次,如下:

byte[] bs = couchbaseShortDao.get("b1f66b668be");
if (bs != null) {
FeatureStatisticsPB.UserHistory ufClickList = FeatureStatisticsPB.UserHistory.parseFrom(bs);
if (ufClickList != null) {
FeatureStatisticsPB.UserHistory.Builder newBuilder = FeatureStatisticsPB.UserHistory.newBuilder(ufClickList);

List<FeatureStatisticsPB.HistoryCell.Builder> valueBuilderList = newBuilder.getValueBuilderList();
Iterator<FeatureStatisticsPB.HistoryCell.Builder> iterator = valueBuilderList.iterator();
while(iterator.hasNext()) {
iterator.next();
iterator.remove();
}
System.out.println(valueBuilderList.size());
}
} else {
System.out.println("null");
}

上面使用了getValueBuilderList,返回的不是 unmodifiableList集合视图(如果使用getValueList方法,返回的是视图),但是在使用迭代器的时候remove同样报java.lang.UnsupportedOperationException异常,进入pb生成的源码中发现,getValueBuilderList返回的是一个MessageOrBuilderExternalList类型list,该类继承AbstractList,但是没有实现remove方法。

4、在循环中删除元素:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
for(int i=0;i<list.size();i++){
list.remove(i);
}
System.out.println(list);

输出的结果是[b,d],和预期不一致。在看下面方式

public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b",
"c", "d"));
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("a")) {
list.remove(i);
}
}
System.out.println(list);
}

输出:[a, b, c, d],于预期也不一致。再看

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
for(String s:list){
if(s.equals("a")){
list.remove(s);
}
}

使用这种方式,我们上面介绍过了,会报ConcurrentModificationException异常。

原因是当一个元素被删除时,列表的大小缩小并且下标变化,所以当你想要在一个循环中用下标删除多个元素的时候,它并不会正常的生效。正确方式:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String s = iter.next();
if(s.equals("a")){
iter.remove();
}
}