Java增强for遍历LinkedList所引发的思考

来源:互联网 发布:万国数据怎么样 编辑:程序博客网 时间:2024/06/06 16:45

上一篇我比较了普通for和增强for之间的区别,效率和应用场景。但其中有一个问题引发了我的思考。就是网上都说用增强for遍历LinkedList会出现ConcurrentModificationException,然而我写的代码却不会出现,我的代码如下:

LinkedList<String> list = new LinkedList<>();        list.add("sky_100");        list.add("a");        list.add("b");        list.add("c");        for (String str : list) {            if ("b".equals(str))                list.remove(str);        }        for(String str: list){            System.out.println(str);        }

运行结果:
这里写图片描述
没报错呀,难道网上说的都是错的?顿时有种众人皆醉我独醒的感觉,哈哈。想想还是多试几下,确认比较好,于是我又试了一段代码:

LinkedList<String> list = new LinkedList<>();        list.add("sky_100");        list.add("a");        list.add("b");        list.add("c");        for (String str : list) {            if ("a".equals(str))                list.remove(str);        }        for (String str : list) {            System.out.println(str);        }

没改动什么,就是把if判断把“b”改成了“a”;
运行结果:
这里写图片描述
真是有意思,接下来就来解释原因吧:
首先来看这段出错代码反编译后的源码:

LinkedList linkedlist = new LinkedList();        linkedlist.add("sky_100");        linkedlist.add("a");        linkedlist.add("b");        linkedlist.add("c");        Iterator iterator = linkedlist.iterator();        do        {            if (!iterator.hasNext())                break;            String s = (String)iterator.next();            if ("a".equals(s))                linkedlist.remove(s);        } while (true);        String s1;        for (Iterator iterator1 = linkedlist.iterator(); iterator1.hasNext(); System.out.println(s1))            s1 = (String)iterator1.next();

经过调试和查看LinkedList源码发现:每次调用next()方法时会做判断,源码如下:

public E next() {            checkForComodification();            if (!hasNext())                throw new NoSuchElementException();            lastReturned = next;            next = next.next;            nextIndex++;            return lastReturned.item;        }

再看checkForComodification()方法源码:

final void checkForComodification() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();        }

这两个方法都是LinkedList的内部类ListItr的方法,原来当modCount 和expectedModCount不相等时会抛出异常。那这两个数为什么会不相等呢,答案是在调用remove()方法的时候改了modCount的值,口说无凭,源码为证:

public boolean remove(Object o) {        if (o == null) {            for (Node<E> x = first; x != null; x = x.next) {                if (x.item == null) {                    unlink(x);                    return true;                }            }        } else {            for (Node<E> x = first; x != null; x = x.next) {                if (o.equals(x.item)) {                    unlink(x);                    return true;                }            }        }        return false;    }

remove调用了unlink()方法,看看unlink()源码:

E unlink(Node<E> x) {         ......        x.item = null;        size--;        <span style="color:#F00">modCount++;</span>        return element;    }

哈哈,找出来了,那么问题来了,既然每次移除后modCount都会改变,而每次next()都会检查,按理说每次remove后都会报错,为啥我第一次的不会报错呢,答案是:因为“b”在倒数第二个元素,每次调用方法hasNext()的时候

 public boolean hasNext() {            return nextIndex < size;        }

判断的是nextIndex是否小于当前集合的大小,然而在调用remove()时:在remove调用了unlink方法,上面讲过了,而unlink方法中

E unlink(Node<E> x) {       ......        x.item = null;        <span style="color:#F00">size--;</span>        modCount++;        return element;    }

所以这下明白了吧,当删出“b”的时候,集合的大小已经变为3了,当调用hasNext()时

 public boolean hasNext() {            return nextIndex < size;//return 3<3        }

返回的是false,根本没机会去调用next()方法检查modCount 和expectedModCount是否相等。
好了,问题解决了,开心!
启发,遇到问题多从源码中找答案,既可以增加自己阅读代码的能力,又让自己的答案十分可信,因为那就是权威,比任何网上说的都有效。

原创粉丝点击