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

上一篇我比较了普通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是否相等。
好了,问题解决了,开心!
启发,遇到问题多从源码中找答案,既可以增加自己阅读代码的能力,又让自己的答案十分可信,因为那就是权威,比任何网上说的都有效。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值