面试题:Iterator遍历的添加删除
2016-06-11 08:50
330 查看
list是一个ArrayList的对象,哪个选项的代码填到//todo delete处,可以在Iterator遍历的过程中正确并安全的删除一个list中保存的对象?()
如果在循环的过程中调用集合的remove()方法,就会导致循环出错,例如:
循环过程中list.size()的大小变化了,就导致了错误。
所以,如果你想在循环语句中删除集合中的某个元素,就要用迭代器iterator的remove()方法,因为它的remove()方法不仅会删除元素,还会维护一个标志,用来记录目前是不是可删除状态,例如,你不能连续两次调用它的remove()方法,调用之前至少有一次next()方法的调用。
源码是这么描述的:ArrayList 继承了 AbstractList, 其中AbstractList 中有个modCount 代表了集合修改的次数。在ArrayList的iterator方法中会判断 expectedModCount与 modCount是否相等,如果相等继续执行,不相等报错,只有iterator的remove方法会在调用自身的remove之后让 expectedModCount与modCount再相等,所以是安全的。
在使用set/map时,一个可爱的小bug:java.util.ConcurrentModificationException
【错误场景1】:set容器,边遍历,边add/remove元素
}
【错误场景2】:map容器,边遍历,边remove元素
【错误场景3】list容器,边遍历,边add/remove元素
【错误原因】
对于remove操作,list.remove(o)的时候,只将modCount++,而expectedModCount值未变,那么迭代器在取下一个元素的时候,发现该二值不等,则抛ConcurrentModificationException异常。
对于add操作,同remove
【解决办法】
remove:用iterator提供的原生态remove()
add:同remove就错了,iterator没有提供原生的add()方法。要用新的容器暂存,然后再遍历结束后,全部添加到原容器当中。
set/list:这两类常用容器,就用上面说的方法remove(), add()就好了。
map:直接使用ConcurrentHashMap就ok。为什么别的容器,不也实现个concurrent版本直接用。。?库里不搞,自己搞。
【正确使用案例】
Iterator it = list.iterator(); int index = 0; while (it.hasNext()) { Object obj = it.next(); if (needDelete(obj)) //needDelete返回boolean,决定是否要删除 { //todo delete } index ++; }
it.remove(); √ list.remove(obj); × list.remove(index); × list.remove(obj,index); ×
如果在循环的过程中调用集合的remove()方法,就会导致循环出错,例如:
for(int i=0;i<list.size();i++){ list.remove(...); }
循环过程中list.size()的大小变化了,就导致了错误。
所以,如果你想在循环语句中删除集合中的某个元素,就要用迭代器iterator的remove()方法,因为它的remove()方法不仅会删除元素,还会维护一个标志,用来记录目前是不是可删除状态,例如,你不能连续两次调用它的remove()方法,调用之前至少有一次next()方法的调用。
源码是这么描述的:ArrayList 继承了 AbstractList, 其中AbstractList 中有个modCount 代表了集合修改的次数。在ArrayList的iterator方法中会判断 expectedModCount与 modCount是否相等,如果相等继续执行,不相等报错,只有iterator的remove方法会在调用自身的remove之后让 expectedModCount与modCount再相等,所以是安全的。
在使用set/map时,一个可爱的小bug:java.util.ConcurrentModificationException
【错误场景1】:set容器,边遍历,边add/remove元素
Set<String> set = new HashSet<String>(); for (int i = 0; i < 10000; i++) { set.add(Integer.toString(i)); } for (String str : set) { //或使用iterator来循环,JDK5.0以上,这样的遍历底层也都是iterator实现。 set.add("xxx"); //报错 //set.remove(str); //报错
}
【错误场景2】:map容器,边遍历,边remove元素
Map<String, String> map = new HashMap<String, String>(); for (int i = 0; i < 100; i++) { map.put(Integer.toString(i), Integer.toString(i)); } for (String str : map.keySet()) {//或使用iterator来循环 map.remove(str); //报错 }
【错误场景3】list容器,边遍历,边add/remove元素
List<String> list = new ArrayList<String>(); for (int i = 0; i < 100; i++) { list.add(Integer.toString(i)); } for (Iterator<String> it = list.iterator(); it.hasNext();) { String val = it.next(); if (val.equals("5")) { list.add(val); //报错 //list.remove(val); //报错 } }
【错误原因】
对于remove操作,list.remove(o)的时候,只将modCount++,而expectedModCount值未变,那么迭代器在取下一个元素的时候,发现该二值不等,则抛ConcurrentModificationException异常。
对于add操作,同remove
【解决办法】
remove:用iterator提供的原生态remove()
add:同remove就错了,iterator没有提供原生的add()方法。要用新的容器暂存,然后再遍历结束后,全部添加到原容器当中。
set/list:这两类常用容器,就用上面说的方法remove(), add()就好了。
map:直接使用ConcurrentHashMap就ok。为什么别的容器,不也实现个concurrent版本直接用。。?库里不搞,自己搞。
【正确使用案例】
for (Iterator<String> it = list.iterator(); it.hasNext();) { String val = it.next(); if (val.equals("5")) { it.remove(); } } List<String> newList = new ArrayList<String>(); for (Iterator<String> it = list.iterator(); it.hasNext();) { String val = it.next(); if (val.equals("5")) { newList.add(val); } } list.addAll(newList);
相关文章推荐
- 一个关于if else容易迷惑的问题
- 一道sql面试题附答案
- C# 超高面试题收集整理
- 浅析iterator与指针的区别
- 人人网javascript面试题 可以提前实现下
- PHP中设置一个严格30分钟过期Session面试题的4种答案
- 据说是雅虎的一份PHP面试题附答案
- php牛逼的面试题分享
- 一套比较完整的javascript面试题(部分答案)
- java使用iterator遍历指定目录示例分享
- Java设计模式之Iterator模式介绍
- 10个经典的Java main方法面试题
- java集合迭代器Iterator中的remove陷阱
- 小米公司JavaScript面试题
- 超级全面的PHP面试题整理集合第1/2页
- 极易被忽视的javascript面试题七问七答
- 5个实用的shell脚本面试题和答案
- PHP经典面试题集锦
- 8个PHP数组面试题
- PHP中提问频率最高的11个面试题和答案