Iterator 接口 也是Java集合框架成员,它主要用来遍历(即迭代访问)Collection集合中的元素,Iterator 对象也称为迭代器
boolean hasNext(): 如果迭代中的集合元素没有被遍历完,则返回
true
Object next(): 返回集合中的下一个元素
- void remove(): 删除集合里上一次next方法返回的元素
Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
for进行删除
- 可以正常删除元素
- 由于执行list.remove(i),list的长度减少了1,若是直接返回list.get(i)返回的是list.get(i+1)的值
- 如果想正常的获取删除的值可以先定义
String str =null
,再用str = list.get(i)
,最后在执行remove方法就可以获取删除元素的值了
list里面有a,b,c,d,e 元素
@Test
public void testForRemove() {
forRemove(list, "b");
for(String str:list) {
System.out.print(str+"\t");
}
}
/*
* 1. list.remove()成功时,list的长度变小一位
*
* 2.这里获取删除元素的值,list.get(i)获取删除的值,变为了list.get(i+1)的值
*/
public static void forRemove(List<String> list,String target){
for(int i = 0;i<list.size();i++){
if(list.get(i).equals(target)){
list.remove(i);
//本来应该输出b,但是却输出c
System.out.print(list.get(i)+"\t");
}
}
}
forRemove
这种方法,数据量大时,效率特别低
Iterator删除
public static void IteratorRemove(List<String> list,String target){
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String s = iterator.next();
if(s.equals(target)){
iterator.remove();
}
}
}
Itreator 必须依附于Collection 对象,Iterator 迭代器采用的是快速失败(fail-fast)机制,一旦迭代过程中检测到该集合已经被修改(通常是程序中的其他线程修改),程序立即引发ConcurrentModificationException异常,而不是显示修改后的结果,这样可以避免共享资源引发的潜在问题
注意:
foreach(加强for循环)进行删除会出问题
循环迭代访问元素集合更加便捷,但是foreach循环中迭代变量也不是集合本身,当进行删除操作时,直接使用list.remove(),并不能在用于删除集合。对于加强for循环,经过编译后采用的是迭代器模式,来遍历,当进行删除,修改时会引发fail-fast(快速失败)
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (String str : list) {
if(str.equals("aaa")) {
list.remove("aaa");
}
}
}
}
反编译后可以看到,foreach使用的
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
Iterator var3 = list.iterator();
while (var3.hasNext()) {
String str = (String) var3.next();
if (str.equals("aaa")) {
list.remove("aaa"); //直接使用list.remove
}
}
}
安全失败(fail—safe)
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。
缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
参考1: Hollis直面java第45期
参考2: Java疯狂讲义