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

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

注:黄色字体为我添的

CopyOnWriteArrayList,因何而存在?

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

CopyOnWriteArrayList,是因”并发”而生。

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

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

原因: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方法中直接抛出异常,说明不支持该操作,至于为什么这样做还待以后慢慢体会。

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

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

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

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

遍历ArrayList时同时修改引发的问题的更多相关文章

  1. 写一段代码在遍历 ArrayList 时移除一个元素?

    该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法.这有一段示例代码,是使用正确的方式来实现在遍历的过程中移 除元素,而不会出现 ...

  2. 遍历ArrayList数组时可能存在的问题

    我们都知道ArrayList类中有个重要的方法是Add(),该方法用于向集合中添加元素,它有一个object类型的参数,表示通过该方法可以向集合中添加任意类型的项,由于ArrayList动态数组中的元 ...

  3. Java_LIST使用方法和四种遍历arrayList方法

    1.List接口提供的适合于自身的常用方法均与索引有关,这是因为List集合为列表类型,以线性方式存储对象,可以通过对象的索引操作对象.   List接口的常用实现类有ArrayList和Linked ...

  4. 使用ArrayList时代码内部发生了什么(jdk1.7)?

    前言 ArrayList(这里的ArrayList是基于jdk1.7)是在项目中经常使用的集合类,例如我们从数据库中查询出一组数据.这篇文章不去剖析它的继承和实现,只是让我们知道实例化及增删改查时它的 ...

  5. Java集合——遍历集合元素并修改

    Java集合——遍历集合元素并修改 摘要:本文主要总结了遍历集合的方式,以及在遍历时修改集合要注意的问题. 遍历Collection 对List和Set的遍历,有四种方式,下面以ArrayList为例 ...

  6. Lambda 表达式遍历集合时用remove方法删除list集合中满足条件的元素问题

    一:循环遍历list集合的四种方式 简单for循环 iterator循环 增加for循环 Lanbda表达式 二:四种遍历方式的用法示例 //简单for循环 List<SalaryAdjustm ...

  7. php 中遍历数组时使用引用出现的问题

    今天在使用foreach遍历数组时发现,当使用&时会出现问题: $arr = array( array('id' => 100, 'error'=> 'aa'), array('i ...

  8. 迭代器 iterator(二): 用iterator遍历arraylist

           迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址.迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些 ...

  9. 【ITOO 2】使用ArrayList时的注意事项:去除多余的null值

    问题描述:在课表导入的时候,将数据从excel表里读出,然后将list批量插入到对应的课程表的数据表单中去,出现结果:当我们导入3条数据时,list.size()为3,但是实际上,list里面存在10 ...

随机推荐

  1. 创建maven工程时总是带有后缀名Maven Webapp解决办法

    做项目时突然遇到了一个新问题,从前没有的,今天不知怎么了突然有了这个问题,maven创建web项目时多出了后缀名maven webapp ,很碍眼,而且访问路径还得删了,这个后缀名才可访问,所以找了答 ...

  2. Mac Pro 编译安装 Redis-3.2.3

    Redis官方下载地址:http://redis.io/download Redis安装 cd /usr/local/src/redis-3.2.3 sudo make sudo make insta ...

  3. ASP.NET Core--条件处理程序中的依赖注入

    翻译如下: 在配置期间(使用依赖注入),授权处理程序必须在服务集合中注册. 假设您有一个在授权处理程序中要解析规则的仓储库,并且该仓储库已在服务集合中注册. 授权将在构造函数还原并注入. 例如,如果你 ...

  4. jquery版小型婚礼(可动态添加祝福语)

    前两天在网上不小心看到“js许愿墙”这几个字,我的神经就全部被调动了.然后就开始我的百度生涯,一直寻觅许愿墙背景图片和便利贴图片,觅了好久……一直没找到满意的……无意间看到祝福语和一些卡通婚礼图片.最 ...

  5. PHP学习-链接数据库

    链接数据库文件:conn.php <?php $conn = mysql_connect("localhost:3306","root","us ...

  6. python 小试题

    有个同事要帮一个朋友做一个小试题,题目如图: 由于个人在学习python路上,所以想用python 写出来这道题,来练练手,苦思冥想,再加上受同事的一些启发,加以扩展,写出代码如下: #!/usr/b ...

  7. ASM:《X86汇编语言-从实模式到保护模式》第17章:保护模式下中断和异常的处理与抢占式多任务

    ★PART1:中断和异常概述 1. 中断(Interrupt) 中断包括硬件中断和软中断.硬件中断是由外围设备发出的中断信号引发的,以请求处理器提供服务.当I/O接口发出中断请求的时候,会被像8259 ...

  8. Android Studio增加NDK代码编译支持--Mac环境

    Android的APP开发基本都是使用Java或者跨平台框架进行开发的,对于很多APP来说已经足够了,但是,对于提供功能给外部使用或者性能要求很高的需求下,如图像处理等,可能会需要C/C++库的支持, ...

  9. 3.通过现有的PDB创建一个新的PDB

    实验说明:创建PDB除了可以通过种子PDB创建外,现在测试通过一个现有的用户PDB克隆创建新的PDB数据库 实验步骤: 1.创建测试数据 SQL> alter session set conta ...

  10. IBatis.Net使用总结(一)-- IBatis解决SQL注入(#与$的区别)

    IBatis解决SQL注入(#与$的区别) 在IBatis中,我们使用SqlMap进行Sql查询时,需要引用参数,在参数引用中可以使用两种占位符#和$.这两种占位符有什么区别呢? (1):#***#, ...