遍历集合时删除集合中的元素问题

使用迭代器Iterator遍历集合元素时,如果删除的元素不是倒数第二个数据,则会抛出ConcurrentModificationException异常

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");list.add("b");list.add("c");
        for (Iterator<String> it = list.iterator();it.hasNext();){
            String first = it.next();
            if ("a".equals(first)) {
                list.remove(first);
            }
            System.out.println(first);
        }
    }

以上代码运行结果如下图:

 

原因:由于迭代器只负责对各种集合所包含的元素进行迭代,它自己并没有保留集合元素。它判断是否还有下一个元素的标准很简单:如果下一步即将访问的元素的索引不等于集合的大小,就会返回true,否则,返回false。当程序使用迭代器遍历集合的倒数第2个元素时,下一步即将访问的元素的索引为size()-1。如果此时通过List删除集合的任意一个元素,将导致集合size()变成size()-1,这将导致hasNext()方法返回false,也就是说遍历会提前结束,永远访问不到最后一个元素。迭代器在获取下一个元素的next()方法中,会调用checkForComodification()方法来检查集合是否被修改:遍历之前使用expectedModCount保留该集合被修改的次数,每次获取集合的下一个元素之前,检查集合的当前修改次数modCount与遍历之前的修改次数expectedModCount是否相等,如果不相等就直接抛出ConcurrentModification异常。源码如下

hasNext()源码:

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

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];
        }
       // 如果修改了,直接抛异常
       final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

 

如果使用普通的for循环删除集合中元素,也会出现数据丢失问题:

 List<String> list = new ArrayList<>();
 list.add("a");list.add("b");list.add("c");
 for (int i = 0;i<list.size();i++) {
     String first = list.get(i);
     System.out.println(first);
     if ("a".equals(first)) {
         list.remove(first);
     }
 }
// 输出结果为 a  c

上述代码为什么丢失了元素b呢?原因是:删除元素a之后,集合中剩余的元素会“整体搬家”,所在的角标都会往前移动一位,此时元素b的角标由1变成了0,元素c的角标由2变成了1,而变量i此时的值为1,因此访问时丢失了元素b

 

使用增强版的for循环会出现和Iterator遍历时相同的问题

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

 

如果想删除集合中的某些元素,可以先定义一个变量,在遍历集合时,将需要删除的元素都添加到该变量中,在循环外统一删除。

        List<String> list = new ArrayList<>();
        list.add("a");list.add("b");list.add("c");
        List<String> deleteList = new ArrayList<>();
        for (String value:list) {
            if ("a".equals(value)) {
                deleteList.add(value);
            }
        }
        if (deleteList.size() > 0) {
            list.removeAll(deleteList);
            deleteList = null;
        }
        // 输出结果为  b  c 
        for (String value:list) {
            System.out.println(value);
        }

 

posted on 2020-09-01 11:08  永不宕机  阅读(874)  评论(1编辑  收藏  举报

导航