文章目录
- 1. Iterator和ListIterator
- 1.1 简介
- 1.2 常用方法
- 2. remove方法
- 2.1 比较foreach方式和迭代器方式删除元素
- 2.2 找原因 -- 迭代器删除操作源码
1. Iterator和ListIterator
1.1 简介
1.Iterator 可以遍历List集合,也可以遍历Set集合; ListIterator只能遍历List集合
2.Iterator 只能单向遍历(向后遍历),ListIterator双向遍历(向前/向后遍历)
3.ListIterator继承Iterator接口,添加新的方法
1.2 常用方法
-
add(E e):将指定的元素插入到集合中,插入位置为迭代器当前位置之前
-
hasNext():正向遍历集合,判断后面是否还有元素,返回boolean类型值
-
next():返回集合中迭代器指向后面位置的元素
-
nextIndex():返回集合中迭代器后面位置元素的索引
-
hasPrevious():反向遍历集合,判断前面是否还有元素,返回boolean类型值
-
previous():返回集合中迭代器指向前面位置的元素
-
previousIndex():返回集合中迭代器前面位置元素的索引
-
set(E e):替换迭代器当前位置的元素
List<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
for (Iterator<String> iterator= list.iterator();iterator.hasNext();){
System.out.println(iterator.next());
}
List<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
//正向遍历
ListIterator<String > iterator=list.listIterator();
iterator.add("EEE");
for (String s : list) {
System.out.println(s);//EEE aa bb cc dd
}
List<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
ListIterator<String > iterator=list.listIterator();
iterator.next();
iterator.add("EEE");
for (String s : list) {
System.out.println(s);//aa EEE bb cc dd
}
System.out.println("-------------------");
iterator=list.listIterator();
for (;iterator.hasNext();){
System.out.println(iterator.next());
}
System.out.println("-------------------");
iterator=list.listIterator();
for (;iterator.hasNext();){
System.out.println(iterator.nextIndex()+"\t"+iterator.next());
}
运行结果:
//反向遍历
ListIterator<String> iterator=list.listIterator(list.size());
while (iterator.hasPrevious()) {
System.out.println(iterator.previousIndex()+"\t"+iterator.previous());
}
运行结果:
ListIterator<String> iterator=list.listIterator(list.size());
while (iterator.hasPrevious()) {
System.out.println(iterator.previousIndex()+"\t"+iterator.previous());
}
iterator.set("GGG");
System.out.println(list);//[GGG, bb, cc, dd]
----------------------------------------------------------------------------
ListIterator<String> iterator=list.listIterator(list.size());
iterator.previous();
iterator.set("GGG");
while (iterator.hasPrevious()) {
System.out.println(iterator.previousIndex()+"\t"+iterator.previous());
}
System.out.println(list);//[aa, bb, cc, GGG]
2. remove方法
2.1 比较foreach方式和迭代器方式删除元素
对集合元素进行循环处理增加或删除时,不能使用foreach处理方式,要使用迭代器方式。
- 使用foreach方法进行删除,报错:java.util.ConcurrentModificationException
ArrayList<String> list=new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for (String s : list) {
if ("aaa".equals(s)){
list.remove(s);
}
}
System.out.println(list);
- 使用迭代器方式删除元素:
Iterator<String> iterator=list.iterator();
while (iterator.hasNext()){
String x=iterator.next();
if (x.equals("aaa")){
iterator.remove();
}
}
System.out.println(list);//[bbb,ccc,ddd]
- 但是,在foreach对集合中倒数第二个元素进行删除时,不会报错,其他位置的元素都会报错。
for (String s : list) {
if ("ccc".equals(s)){
list.remove(s);
}
}
System.out.println(list);//[aaa,bbb,ddd]
其中,foreach底层也是通过迭代器实现的。
其实,我们在使用迭代器操作时,有两个步骤:
iterator.hasNext();//判断是否有下一个元素
item=iterator.next();//有下一个元素的话,取出
2.2 找原因 – 迭代器删除操作源码
首先,我们进入到Iterator的remove()方法中,找到Itr类,在这个类中我们会看到ArrayList的remove()方法
在remove()方法中,看到里面有个checkForComodification()方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
在checkForComodification()方法中,有两个变量:modCount变量和expectedModCount变量,当这两个变量不相同时,就会抛出异常处理(也就是我们在前面遇到的那个异常)
modCount变量:记录集合对象从new出来到现在被修改的次数
expectedModCount变量:迭代器现在期望这个集合被修改的次数
在上面源码中看到:
expectedModCount = modCount;
只要保持这两个变量相等,删除操作就没有问题。
所以,迭代器不会报错,使用foreach会报错,原因是:
在迭代器中,对这两个变量进行了同步处理,而foreach没有进行同步处理,导致会出现checkForComodification异常出现。
通过查看源码,有一个hasNext()方法,方法中要比较cursor和size
public boolean hasNext() {
return cursor != size;
}
cursor是下一个元素的索引值;size是整个集合元素个数
如果两个元素不相等,即有下一个元素,就返回true。
当删除倒数第二个元素时,cursor通过计算之后,得到cursor=size,导致迭代器认为不存在下一个元素,迭代结束。
整体架构如下: