遍历ArrayList时同时修改引发的问题
看见一篇博客,没有写完整,于是增补了一下:
博客原文:http://www.cnblogs.com/alipayhutu/archive/2012/08/11/2634073.html
注:黄色字体为我添的
CopyOnWriteArrayList,因何而存在?
ArrayList的一个线程安全的变体,其所有可变操作(add、set 等)都是通过对底层数组进行一次新的复制来实现的,代价昂贵。
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时同时修改引发的问题的更多相关文章
- 写一段代码在遍历 ArrayList 时移除一个元素?
该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法.这有一段示例代码,是使用正确的方式来实现在遍历的过程中移 除元素,而不会出现 ...
- 遍历ArrayList数组时可能存在的问题
我们都知道ArrayList类中有个重要的方法是Add(),该方法用于向集合中添加元素,它有一个object类型的参数,表示通过该方法可以向集合中添加任意类型的项,由于ArrayList动态数组中的元 ...
- Java_LIST使用方法和四种遍历arrayList方法
1.List接口提供的适合于自身的常用方法均与索引有关,这是因为List集合为列表类型,以线性方式存储对象,可以通过对象的索引操作对象. List接口的常用实现类有ArrayList和Linked ...
- 使用ArrayList时代码内部发生了什么(jdk1.7)?
前言 ArrayList(这里的ArrayList是基于jdk1.7)是在项目中经常使用的集合类,例如我们从数据库中查询出一组数据.这篇文章不去剖析它的继承和实现,只是让我们知道实例化及增删改查时它的 ...
- Java集合——遍历集合元素并修改
Java集合——遍历集合元素并修改 摘要:本文主要总结了遍历集合的方式,以及在遍历时修改集合要注意的问题. 遍历Collection 对List和Set的遍历,有四种方式,下面以ArrayList为例 ...
- Lambda 表达式遍历集合时用remove方法删除list集合中满足条件的元素问题
一:循环遍历list集合的四种方式 简单for循环 iterator循环 增加for循环 Lanbda表达式 二:四种遍历方式的用法示例 //简单for循环 List<SalaryAdjustm ...
- php 中遍历数组时使用引用出现的问题
今天在使用foreach遍历数组时发现,当使用&时会出现问题: $arr = array( array('id' => 100, 'error'=> 'aa'), array('i ...
- 迭代器 iterator(二): 用iterator遍历arraylist
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址.迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些 ...
- 【ITOO 2】使用ArrayList时的注意事项:去除多余的null值
问题描述:在课表导入的时候,将数据从excel表里读出,然后将list批量插入到对应的课程表的数据表单中去,出现结果:当我们导入3条数据时,list.size()为3,但是实际上,list里面存在10 ...
随机推荐
- Eclipse快捷键与使用技巧总结
快捷键 功能 Ctrl + / 双斜杠注释 Ctrl + Shilft + / 块注释 Alt + / 代码提示 Ctrl + I (或 Ctrl + Shift + F) 格式化代码 Ctrl + ...
- 【荐1】Total Commander 7.57 个人使用设置 及 常用快捷键 备忘
Total Commander 7.57a 下载地址:http://www.baidu.com/s?wd=total commander 7.57 破解版 软件整体预览图:(注意,下面的版本我用的是 ...
- C和指针 第十四章 习题
14.1 打印函数 #include <stdio.h> void print_ledger_long(){ printf("function print_ledger_long ...
- django 模板语法和三种返回方式
模板 for循环 {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} if语句 ...
- ORA-27101 ORACLE not available
问题描述:今天打开plsql,报错无法识别连接服务 1.然后去找,是不是oracle服务没有启动,但是发现oracle的其他服务都能启动.只有OracleDBConsolearies不能启动,这说明O ...
- Func与Action
平时我们如果要用到委托一般都是先声明一个委托类型,比如: private delegate string Say(); string说明适用于这个委托的方法的返回类型是string类型,委托名Say后 ...
- WPF CollectionViewSource CollectionView
CollectionView 通俗讲就是可以对你绑定的集合可以进行 分组,排序 等功能 CollectionViewSource 根据字面意思是xxx的数据源 详细的介绍还是看 http://www ...
- 进程互斥和fork
自父进程继承 进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs)) 环境(environment) 堆栈 内存 打开文件的描述符(注意 ...
- go:interface{}、断言与类型转换
interface{}可用于向函数传递任意类型的变量,但对于函数内部,该变量仍然为interface{}类型(空接口类型), 不清楚这点将可能导致错误.如以下代码: package main impo ...
- SQL的ROW_NUMBER函数
with tabs as ( select ROW_NUMBER() over(partition by customerID order by totalPrice) as rows,custome ...