黑马程序员技术交流社区

标题: 一个集合运行的问题 [打印本页]

作者: 王红潮    时间: 2012-9-16 09:38
标题: 一个集合运行的问题
本帖最后由 王红潮 于 2012-9-16 09:45 编辑
  1. import java.util.*;
  2. class ListDemo
  3. {
  4. public static void main(String[] args)
  5. {
  6. ArrayList al = new ArrayList();

  7. al.add("java01");
  8. al.add("java02");
  9. al.add("java03");
  10. al.add("java04");

  11. Iterator it = al.iterator();
  12. while (it.hasNext())
  13. {
  14. Object obj = it.next();

  15. if (obj.equals("java02"))
  16. al.add("java09");
  17. System.out.println("obj="+obj);
  18. }
  19. }
  20. }

  21. /*
  22. 运行结果:
  23. obj=java01
  24. obj=java02
  25. Exception in thread "main" java.util.ConcurrentModificationException
  26. at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  27. at java.util.ArrayList$Itr.next(Unknown Source)
  28. at ListDemo.main(ListDemo.java:17)

  29. */
复制代码
//迭代器内不是应该先进行if判断吗,为什么还会打印obj=java02?
作者: 小黑马    时间: 2012-9-16 10:11
本帖最后由 马小龙 于 2012-9-16 10:12 编辑

以下是API中队ArrayList容器的定义描述
注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

        List list = Collections.synchronizedList(new ArrayList(...)); 此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

package com.itheima;

import java.util.*;

public class Test {
    public static void main(String[] args) {
        ArrayList al = new ArrayList();

        al.add("java01");
        al.add("java02");
        al.add("java03");
        al.add("java04");

        Iterator it = al.iterator();
        while (it.hasNext()) {
            Object obj = it.next();

            if (obj.equals("java02"))
                al.add("java09");   //在这里不能用除迭代器外,任何时间以任何方式对列表进行修改,包括增加和删除
            
            System.out.println("obj=" + obj);
        }
    }

}


作者: 武庆东    时间: 2012-9-16 10:22
针对抛出的异常进行分析:
import java.util.*;
class ListDemo
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();

al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");

Iterator it = al.iterator();
while (it.hasNext())
{
Object obj = it.next();

if (obj.equals("java02"))
al.add("java09");  //修改了modCount的值,导致程序抛出异常
System.out.println("obj="+obj);
}
}
}

/*
运行结果:
obj=java01
obj=java02
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at ListDemo.main(ListDemo.java:17)

*/
我们使用的迭代器在调用next()调用了改方法checkForComodification(),当每次检查modCount != expectedModCount就会抛出该异常,
下面我们来再次分析我们的代码 modCount是集合每次在调用remove和add方法时都会记录修改次数modCount++,但是我们迭代产生后expectedModCount就是一个固定值了,所以你只要在迭代时修改了集合就会造成modCount != expectedModCount,所以就抛出了ConcurrentModificationException
作者: 程振    时间: 2012-9-16 10:31
If a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception——ConcurrentModificationException

"From the field of soft engineering, a Fail Fast Iterator is an Iterator that attempts to raise an error if the sequence of elements precessed by the Iterator is changed during Iteration"

所以你修改集合后Iterator只是被标记成fail-fast iterator,直到下次使用时才会抛出异常,因此会输出第二句话。
作者: 史小兵    时间: 2012-9-16 10:43
package com.itheima;

import java.util.*;

public class Test {
    public static void main(String[] args) {
        ArrayList al = new ArrayList();

        al.add("java01");
        al.add("java02");
        al.add("java03");
        al.add("java04");

        Iterator it = al.iterator();
        while (it.hasNext()) {
            Object obj = it.next();

            if (obj.equals("java02"))
                al.add("java09");   //注意在此处,你使用迭代器进行集合中元素的遍历的过程中是不可以改变,集合中元素的值的(就是不能增删改)因为在遍历时会调用checkForComodification(),在此过程中每次调用都会修改modCount++,所以结束后产生expectedModCount应该是一个固定的值,因此只要你在此过程中改变了该值就会有异常
            System.out.println("obj=" + obj);
        }
    }

}


作者: 王红潮    时间: 2012-9-17 10:03
马小龙 发表于 2012-9-16 10:11
以下是API中队ArrayList容器的定义描述
注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例 ...

可是,它出现异常,为什么不马上停止运行,还会向下执行呢?
作者: 王红潮    时间: 2012-9-17 10:08
orgcheng 发表于 2012-9-16 10:31
If a thread modifies a collection directly while it is iterating over the collection with a fail-fas ...

你英语太立了!
作者: 陈振兴    时间: 2012-9-17 11:01
本帖最后由 陈振兴 于 2012-9-17 11:02 编辑

有两点:
1.集合是面向对象的,List是面向接口的,
所以while (it.hasNext()) { //这里就相当于指针一样,有就往下读,没有就返回
            Object obj = it.next();
                                          //所以就会在这里打印前两个值,
            if (obj.equals("java02")) //读到第二值时,你这里比较两个值是否相等,说明已经读到这里了
                al.add("java09");   //报错就是在遍历对象的时候他不支持增删改的操作,这里是遍历所有结果。所以就会抛出修改异常的错误
            
            System.out.println("obj=" + obj);
        }
2.在所有集合中遍历对象的时候是不支持修改动作的!把这块理清了就行!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2