欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

你所熟悉又不熟悉的加强for循环

程序员文章站 2024-03-20 10:42:04
...

众所周知,在加强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
你所熟悉又不熟悉的加强for循环
这明显是false,于时,不进入next方法,然后也不去判断checkForComodification方法, 当然也不会去抛出异常(即使,现在4!=3是成立的)。于时,删除成功!但是,也有一个问题,那就是最后一个元素无法在本次循环中得到。我们可以将str打印出来证实。但是,会在下次循环中重新被打印出来,因为初始化,expectedModCount = modCount;所以,再次循环的时候,这两个值都等于4(此时,只有a和c两个值).
你所熟悉又不熟悉的加强for循环

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)。