• 遍历ArrayList时同时修改引发的问题


    看见一篇博客,没有写完整,于是增补了一下:

    博客原文:http://www.cnblogs.com/alipayhutu/archive/2012/08/11/2634073.html

    注:黄色字体为我添的

    CopyOnWriteArrayList,因何而存在?

    ArrayList的一个线程安全的变体,其所有可变操作(addset 等)都是通过对底层数组进行一次新的复制来实现的,代价昂贵。

    CopyOnWriteArrayList,是因”并发”而生。

    【场景一】对于ArrayList,使用直接方式,一边遍历,一边删除,会报错。

     // 删除/修改元素
     for(String item : list){
         list.remove(item);
     }

    image

    原因:foreach语法是通过Iterator来实现的,当遍历这个List的时候,会生成一个ArrayList.Itr对象,这个私有内部类实现了Iterator接口,也就是说上面这段代码与下面这段代码效果一样:

      Iterator it = list.iterator() ;

      while(it.hasNext()){

        String temp = it.next() ;

        list.remove(temp) ; 

      }

    在ArrayList中有一变量记录的当前这个ArrayList被修改的次数,每当调用add/remove方法就会把该参数的值加一,当生成Iterator对象时该对象会记录当前状态ArrayList的修改次数,然后在每次调用it.next()时就会判断当前ArrayList修改次数是否和它记录的相同,如果不同就抛出异常。例如上面等效后的代码,当执行 it = list.iterator() ;时 it对象记录在执行这句代码之前list对象的修改次数,当第一次执行it.next()语句时不会发生异常,因为这时list还没有被修改,但是当第二次执行it.next()时list已经通过list.remove(temp)代码修改了其内部的修改次数变量,所以导致it对象记录的修改次数和list的修改次数不同,所以就抛出了异常。

    只需记住的准则是: 使用Iterator遍历集合时是不能修改集合的。

    雨伞 解决办法一:使用迭代器,一边遍历,一边删除,不会报错。

    // 删除/修改元素 
    Iterator<String> it = list.iterator(); 
    while(it.hasNext()){ 
        String ele = it.next(); 
        it.remove(); 

    http://blog.sina.com.cn/s/blog_605f5b4f0100qsgf.html

    使用Iterator的remove方法时不会对集合是否被更改进行判断,所以上面不会出现ModifyException。

    雨伞 解决办法二:使用CopyOnWriteArrayList,直接方式,一边遍历,一会删除,不会报错。

    for(String item : list){ 
        list.remove(item); 

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

    【场景二】对于ArrayList,使用迭代器,一边遍历,一边add,会报错。

    Iterator<String> it = list.iterator(); 
    while(it.hasNext()){ 
        String str = it.next(); 
        String tem = str + "..."; 
        list.add(tem); 

    原因:因为list.add会修改其内部存储的list修改次数变量,导致it内部记录的list修改次数和list当前状态的修改次数不同,所以产生异常。

    雨伞 解决办法一:改用CopyOnWriteArrayList,直接方式,一边遍历,一边add,不会报错。

    for(String item : list){ 
        String tem = item + "..."; 
        list.add(tem); 

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

    直接遍历与使用Iterator的效果是一样的。

    雨伞 解决办法二:改用CopyOnWriteArrayList,一边遍历,一边add,不会报错。

            Iterator<String> it = list.iterator(); 
            while(it.hasNext()){ 
                String str = it.next(); 
                String tem = str + "..."; 
                list.add(tem); 
            }

    【场景三】对于CopyOnWriteArrayList,迭代器,不能remove。

    Iterator<String> it = list.iterator(); 
    while(it.hasNext()){ 
        String str = it.next(); 
        String tem = str + "..."; 
        it.remove(); 

    CopyOnWriteArrayList实现的remove方法中直接抛出异常,说明不支持该操作,至于为什么这样做还待以后慢慢体会。

    image

    雨伞 解决办法一:使用直接方式,一边遍历,一边add/remove()

    for(String item : list){ 
        String tem = item + "..."; 
        list.remove(item); 
        list.add(tem); 

     上面这个解决方案作者写错了,这段代码会抛出ModifyException异常。

    【总结】底层究竟发生着什么?CPU、内存使用如何? to be continued…

  • 相关阅读:
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_10 斗地主案例(双列)_1_斗地主案例的需求分析
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_09 Debug调试_1_Debug追踪
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_11_JDK9对集合添加的优化_of方法
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_9_Hashtable集合
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_8_LinkedHashMap集合
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_7_HashMap存储自定义类型键值
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_6_Map集合遍历键值对方式
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_4_Map集合遍历键找值方式
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_3_Map接口中的常用方法
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_2_Map常用子类
  • 原文地址:https://www.cnblogs.com/caiyao/p/4963206.html
Copyright © 2020-2023  润新知