我是靠谱客的博主 会撒娇衬衫,最近开发中收集的这篇文章主要介绍为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

分析

首先

然后

最后

解决方案

扩充


首先我们来看错误案例

    @Test
    public void doSomething(){
        ArrayList<String> list = new ArrayList<String>();
        list.add("昨天");
        list.add("今天");
        list.add("明天");
        for (String s : list){
            if (s.equals("昨天")){
                list.remove(s);
            }
        }
    }

这个将会报错

java.util.ConcurrentModificationException

分析

首先

调用了三次add,我们来看list中add的源码(不会的可以看我关于源码博文的list讲解),最终调用了modCount++。这个modCount变量就是为了记录操作list的次数,详细可以看源码。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

然后

我们都知道增强for循环,相当于是迭代器的语法糖。ArrayList中迭代器的方法源码

public Iterator<E> iterator() {
        return new Itr();
    }

//Itr为内部类
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount; //赋值给expectedModCount

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        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];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

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

增强for循环相当于在while里调用了hasnext方法以及调用了next的方法,最后调用了外部的remove方法(注意,不是内部类itr的方法,此时expectedModCount为3

调用remove方法时的源码,实质调用了fastRemove,此刻modCount又加1变为了4

private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

最后

还记得上边循环调用了next方法吗,我们来看一下next方法

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];
        }

里边有个checkForComodification()

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

而此刻modCount为4,expectedModCount为3,不相等,所以抛出异常。

解决方案

我们注意到,在Itr内部类中有个remove方法,其中有个代码让expectedModCount=modCount(代码注释标出)

public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount; //是这个代码
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

所以我们需要调用内部类里边的remove。标准代码如下:

    @Test
    public void doSomething(){
        ArrayList<String> list = new ArrayList<String>();
        list.add("昨天");
        list.add("今天");
        list.add("明天");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals("昨天")){
                iterator.remove();
            }
        }
        System.out.println(list);
    }

扩充

这种机制成为“fail-fast”机制,也就是只要出现一个异常,程序及时止损不再往下运行。这种机制一般仅用于检测bug。

OK,今天也是一知半解的一天,如果对你有帮助,赶紧点赞收藏关注。

最后

以上就是会撒娇衬衫为你收集整理的为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充的全部内容,希望文章能够帮你解决为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部