java集合中的一个移除数据陷阱(遍历集合自身并同时删除被遍历数据)
下面是网上的其他解释,更能从本质上解释原因:
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
在java常用的集合框架就是list ,set ,map 。
list 可通过下标进行遍历,set,map不能通过下表进行遍历,因此对于set ,map的数据遍历时,常常采用迭代器,不过在使用迭代器移除数据时存在陷阱。
执行如下代码:
Set set = new HashSet();
set.add(1);
set.add(2);
set.add(3);
Iterator i = set.iterator();
while(i.hashNext()){
Object o = i.next();
set.remove(o);
}
执行结果会出现以下异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
这个问题在集合框架使用迭代器遍历数据时存在,在java5新曾的foreach遍历方式下也存在。在通过下表遍历list的过程中不存在着个问题。
如:
List list = new ArrayList();
list .add(1);
list.add(2);
list.add(3);
for(int i = 0 ; i < list.size();i++){
list.remove(list.get(i));|| list.remove(i);
}
//
或者使用iterator的remove方法
代码能够正常执行。
当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个集合当中,遍历结束之后,统一移除。
------------------------------------------------------------------------------------------------------
利用java迭代器Itetator遍历并删除HashMap中的元素问题
下面的代码试图利用HashMap的Iterator对象遍历该HashMap并删除满足条件的元素(比如超时的元素),但会抛出java.util.ConcurrentModificationException异常
public static void main(String[] args)
{
HashMap<String, String> hs=new HashMap();
hs.put("p1", "1");
hs.put("p2", "1");
hs.put("p3", "1");
hs.put("p4", "1");
hs.put("p5", "1");
hs.put("p6", "1");
Iterator it=hs.keySet().iterator();
while(it.hasNext())
{
String str=(String)it.next();
System.out.println(hs);
//逻辑处理.........
.............
hs.remove(str);
}
}
原因应该是hs.remove(str)后,it内容没变,并且it里的指针列表又重新排序,所以只要确保删除任一元素后,it保持同步更新即可:
解决方案一:删除任一元素后,it保持同步更新
............
Iterator it=hs.keySet().iterator();
while(it.hasNext())
{
it=hs.keySet().iterator();
String str=(String)it.next();
System.out.println(hs);
//逻辑处理.........
.............
hs.remove(str);
}
...........
这样的时间复杂度明显太大(两层循环嵌套)
解决方案二:
由于删除元素时,hs的iterator对象也重新排序,所以只要用hs的一个副本hsBack
Uackp的iterator去遍历hs即可,这样在删除hs元素时iterator就不会重排了(因为删除的是hs的元素,而不是该iterator所属的hsBackUackp)
...................
hsBackUp=(HashMap<String, String>)hs.clone();
Iterator it=hsBackUp.keySet().iterator();
System.out.println(hsBackUp);
while(it.hasNext())
{
String str=(String)it.next();
System.out.println(hs);
hs.remove(str);
}
.....................
这样虽然时间复杂度小了(只有一层循环),可是空间复杂度大了(多了一个hashmap的拷贝);
查阅api文档和相关资料后,原来iterator对象有一remove方法:
void remove()
Removes from the underlying collection the last element returned by the
iterator (optional operation). This method can be called only once per
call to next. The behavior of an iterator is unspecified if
the underlying collection is modified while the iteration is in
progress in any way other than by calling this method.
于是有下面的改进:
解决方案三:
..............................
Iterator it=hs.keySet().iterator();
while(it.hasNext())
{
String str=(String)it.next();
System.out.println(hs);
it.remove();
}
..............................
java集合中的一个移除数据陷阱(遍历集合自身并同时删除被遍历数据)的更多相关文章
- jquery[siblings]取得一个包含匹配的元素集合中每一个元素的所有唯一同辈元素的元素集合
取得一个包含匹配的元素集合中每一个元素的所有唯一同辈元素的元素集合,用于筛选同辈元素的表达式 $("#pageList").click(function(){ $(this).pa ...
- PHP的排列组合问题 分别从每一个集合中取出一个元素进行组合,问有多少种组合?
首先说明这是一个数学的排列组合问题C(m,n) = m!/(n!*(m-n)!) 比如:有集合('粉色','红色','蓝色','黑色'),('38码','39码','40码'),('大号','中号') ...
- 如何在java代码中调用一个web项目jsp或者servlet
有时候需要调用一个web项目的jsp或者servlet,但是执行内部的代码,并不是打开jsp,例如需要在一段java代码中清除一个web项目中的缓存,那么可以把清除缓存的代码放在该web项目的一个se ...
- prev([expr]) 取得一个包含匹配的元素集合中每一个元素紧邻的前一个同辈元素的元素集合。
prev([expr]) 概述 取得一个包含匹配的元素集合中每一个元素紧邻的前一个同辈元素的元素集合. 可以用一个可选的表达式进行筛选.只有紧邻的同辈元素会被匹配到,而不是前面所有的同辈元素.直线电机 ...
- children([expr]) 取得一个包含匹配的元素集合中每一个元素的所有子元素的元素集合。
children([expr]) 概述 取得一个包含匹配的元素集合中每一个元素的所有子元素的元素集合. 可以通过可选的表达式来过滤所匹配的子元素.注意:parents()将查找所有祖辈元素,而chil ...
- Java项目中每一个类都可以有一个main方法
Java项目中每一个类都可以有一个main方法,但只有一个main方法会被执行,其他main方法可以对类进行单元测试. public class StaticTest { public static ...
- c#---部分;把数组或者结构体存入集合里,然后再从集合中取出之后,输出;foreach既可以用到提取数组重点额数据,也可以提取集合中的数据(前提是集合中的元素是相同数据类型)
1.输入班级人数,统计每个人的姓名,性别,年龄:集合与数组 //Console.Write("请输入班级人数:"); //int a = int.Parse(Console.Rea ...
- 使用Java Stream,提取集合中的某一列/按条件过滤集合/求和/最大值/最小值/平均值
不得不说,使用Java Stream操作集合实在是太好用了,不过最近在观察生产环境错误日志时,发现偶尔会出现以下2个异常: java.lang.NullPointerException java.ut ...
- Java List中的一个List选择选择移除方法
记录: 第一个参数:传入需要处理的List 第二个参数:需要处理的参数在List中的标识符 第三个参数:在需要处理的参数中的开始位置 第三个参数:在需要处理的参数中的个数 List<String ...
随机推荐
- python对目录下的文件进行 多条件排序
在进入正题之前,先介绍一下基础知识: 1.sort(),方法:就是对列表内容进行正向排序,直接在原列表进行修改,返回的是修改后的列表 lists =[1, 5, 10, 8, 6]lists.sort ...
- CompletableFuture异步编排
什么是CompletableFuture CompletableFuture是JDK8提供的Future增强类.CompletableFuture异步任务执行线程池,默认是把异步任务都放在ForkJo ...
- 工作流--Activiti
一.工作流 1.工作流介绍 工作流(Workflow),就是通过计算机对业务流程自动化执行管理.它主要解决的是“使在多个参与者 之间按照某种预定义的规则自动进行传递文档.信息或任务的过程,从而实现某 ...
- 机器学习3- 一元线性回归+Python实现
目录 1. 线性模型 2. 线性回归 2.1 一元线性回归 3. 一元线性回归的Python实现 3.1 使用 stikit-learn 3.1.1 导入必要模块 3.1.2 使用 Pandas 加载 ...
- Python第四章-流程控制
流程控制 在以前的代码中,所有的代码都是交由 Python 忠实地从头执行到结束.但是这些远远不够.很多时候需要根据不同的情况执行不同的代码. 如果你想改变这一工作流程,应该怎么做? 就像这样的情况: ...
- 推荐 | 7个你最应该知道的机器学习相关github项目
欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 磐石 目录: 介绍 Person Blocker(人体自动遮挡) ...
- coding++:Spring IOC/DI 实现原理
什么是 SpringIOC: spring ioc 指的是控制反转,IOC容器负责实例化.定位.配置应用程序中的对象及建立这些对象间的依赖.交由Spring容器统一进行管理,从而实现松耦合. “控制反 ...
- Thread wait notify sleep
wait: 必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行 notify/notifyall: 唤醒因锁池中的线程,使之运行 wait与sleep区别 对于sleep()方法,我们 ...
- 一篇漫画故事带你理解透HTTPS(上)
2020年蝙蝠纪元,二毛一如往常的呆在家中,不敢外出去浪. 为排解心中之闷,二毛抽了一口老烟,熟练的打开了全球最大的同性交友网站,准备假装了解下最近流行的项目... 只听啪的一声回车键,哪知浏览器蹦出 ...
- javascript创建函数的方法
函数对任何语言来说都是一个核心的概念.函数,是一种封装(将一些语句,封装到函数里面). 通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行. ECMAScript中的函数使用funct ...