增强的for循环遍历期间删除集合元素的问题

  前几天的京东一面中有个问题,大概意思是:

有一个场景List集合,我向里边加入10个元素
现在我想删除前五个,要求用一次for循环,问问该怎么做到?

  我当时想这道题肯定没有那么简单,应该不是常规思路可以解决的。于是,我给出的我的解决思路是,开辟一个集合把后五个元素加入其中,然后覆盖掉原来的集合。显然这个回答面试官是不满意的。终于找个时间我自己测试了一下子。

import java.util.ArrayList;
import java.util.List;
public class Test6 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        list.add("asdf");
        list.add("bbb");
        list.add("aaa");
        list.add("acd");
        list.add("da");
        list.add("bb");
        list.add("yyy");
        for (int i = 0; i < list.size(); i++) {
            if (i <= 4) {
                list.remove(i);
            }
        }
        for (String a:list) {
            System.out.println(a);
        }
    }
}


结果是:
bbb
acd
bb

  是不是发现和我们的预期相差甚远,哈哈,没错,我也是一脸懵逼。
  下边是我想出来的正确的解决办法,也是网上的正确在集合for循环里删除元素的方法

import java.util.ArrayList;
import java.util.List;
public class Test6 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        list.add("asdf");
        list.add("bbb");
        list.add("aaa");
        list.add("acd");
        list.add("da");
        list.add("bb");
        list.add("yyy");
        int sign = 0;
        for(int i=0;i<list.size();i++){
            list.remove(i);
            i--;
            sign++;
            if (sign == 5){
                break;
            }
        }
        for (String a:list) {
            System.out.println(a);
        }
    }
}

结果:
bb
yyy

  其实背后的原因也很简单,就是list集合是动态的集合,这个动态是指我在删除一个元素的时候那么这个list集合为了保持正确性,集合的长度就要减一,这个元素后边的元素就要向前移动一个位置,就是这个元素后所有元素的下标减一。所以那句话:‘’for循环只能用来遍历集合,不可以在遍历的同时进行修改和删除。‘’你理解了吗?

--------------------------------------------------------------------------------------------------------------------拓展一下:
讲一下,为什么foreach循环也不能在遍历集合的时候进行删除元素的操作呐?

import java.util.ArrayList;
import java.util.List;
public class Test7 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        list.add("asdf");
        list.add("bbb");
        list.add("aaa");
        list.add("acd");
        list.add("da");
        list.add("bb");
        list.add("yyy");
        for (String s:list) {
            if (s.contains("a")) {
                list.remove(s);
            }
        }
        for (String string:list) {
            System.out.println(string + "-----------------");
        }
    }
}


结果是:
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at 秋招.test.Test7.main(Test7.java:28)

深入分析jdk1.8的ArrayList源码的foreach方法

@Override
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

重点看这个

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

我们知道list在进行foreach的时候都会生成一个迭代器就是iterator,由这个迭代器来进行遍历的操作。当然删除的时候也要经过这个迭代器的。而迭代器有两个方法在遍历数据的时候要用到的,一个是iterator.hasNext(); //判读是否有下个元素。另一个是item = iterator.next();//下个元素是什么,并把它赋给item。
我们来看一下这两个方法的源码

		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];
        }
		final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

这样的话,我们就锁定了modCount != expectedModCount所以抛出了异常,而modCount是指什么呐?expectedModCount又是什么呐?从字面的意思上expectedModCount是期望修改的次数。而modCount是实际的修改的次数。
expectedModCount在生成迭代器的时候就已经生成好了,就是集合的元素大小,而在遍历时删除元素,这个modCount值就会改变,所以为了安全起见抛出了异常。

结语:

  希望我的分享对你有所帮助,也欢迎批评指正!

发布了123 篇原创文章 · 获赞 80 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_38101105/article/details/89502879
今日推荐