你所熟悉又不熟悉的加强for循环
众所周知,在加强for循环的时候不可以一边循环,一边删除,但是有特例。
首先摆出结论:可以在加强循环的同时删掉倒数第二个元素
测试代码如下:
package test;
import java.util.ArrayList;
import java.util.List;
public class ForTest {
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
/* for (int i=0;i<list.size();i++){
if ("".equals(list.get(i))){
list.remove(i);
}
}*/
for (String str :list){
System.out.println(str);
if ("b".equals(str)){
list.remove(1);
}
}
for (String str :list){
System.out.println("element: "+str);
}
}
}
删除a元素的时候或者c元素的时候都会报错:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at test.ForTest.main(ForTest.java:17)
Process finished with exit code 1
而普通for循环并没有此现象,为什么呢?
debug下便知道,加强for循环使用的arraylist中的Itr内部类,里面有三个变量
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
最后一行,expectedModCount = modCount;
主要在next方法中进行判断 checkForComodification();
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
如下方法主要是检测,是否异常的标准。
删除a元素场景,首先进入hastnext方法,0!=3,hastnext成立,,执行next方法的时候正常并返回,remove后没问题,此时modCount已经加1了,此时继续hasnext,1!=2,也没问题,但是在执行next()方法,即要返回元素的时候,进行了checkForComodification,发现4!=3。于时报错。
删掉b元素场景(即倒数第二个元素),循环到b的时候,一定是1!=3,hastnext成立,返回remove后,modCount +1 为4,expectedModCount 为3,
然后接着进行循环hasnext,此时就是有趣的时候,判断出现2!=2
这明显是false,于时,不进入next方法,然后也不去判断checkForComodification方法, 当然也不会去抛出异常(即使,现在4!=3是成立的)。于时,删除成功!但是,也有一个问题,那就是最后一个元素无法在本次循环中得到。我们可以将str打印出来证实。但是,会在下次循环中重新被打印出来,因为初始化,expectedModCount = modCount;所以,再次循环的时候,这两个值都等于4(此时,只有a和c两个值).
a
b
element: a
element: c
Process finished with exit code 0
附上checkForComodification 方法,
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
因为,总结出来的结论再说一次,
可以在加强for循环的同时删掉倒数第二个元素
因为,根本原因在于,本该判断最后一个元素存在的时候,即判断hastnext方法的时候, size-1 ! = size -1
第一个size-1,表示本来最后一个元素的下表,即sive-1. 本例子中为2.
第二个size-1,表示的是,相对起初的元素大小(我们这里是3),少了一个元素(就变为了2)。