zoukankan      html  css  js  c++  java
  • 对Collections的遍历删除方式

    以collections的子类List(Arraylist)为例

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

    做一个遍历的操作,如果list中的某个元素满足某个条件,则将该元素从list中移除。

    经过测试运行,可以发现,除非说要移除的元素位于倒数第二的元素,否则会出现异常,报错如下:

    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    	at java.util.ArrayList$Itr.next(Unknown Source)
    	at Main.main(Main.java:49)
    

    再看如下一例子

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

    期望得到的list应该只包含“a”,“b”两个元素,然而得到的是如下的list:

    [a, b, c]

    解决办法:

    定义一个新的list,将要删除的list放在这个新定义的list中,最后调用removeAll的方法整体删除

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

    错误分析:

    首先知道for—each循环是使用迭代器来实现的,找到源码

    //    在AbstractList类中  
    //    内部成员有:  
    //    protected transient int modCount = 0;  
          
    //    注:该modCount相当于集合的版本号,集合内容每改变一次,其值就+1。  
    //    观察下面内部类可以知道,在next()、remove()方法都会去判断集合版本号与迭代器版本号是否一致checkForComodification();  
    //    内部方法有:  
        public Iterator<E> iterator() {  
            return new Itr();  
            }  
    
        private class Itr implements Iterator<E> {  
            int cursor = 0;  
            int lastRet = -1;  
            //***创建迭代器时就讲版本号给了迭代器  
            int expectedModCount = modCount;  
          
            public boolean hasNext() {  
                    return cursor != size();  
            }  
          
            public E next() {  
                    checkForComodification();  
                try {  
                E next = get(cursor);  
                lastRet = cursor++;  
                return next;  
                } catch (IndexOutOfBoundsException e) {  
                checkForComodification();  
                throw new NoSuchElementException();  
                }  
            }  
          
            public void remove() {  
                if (lastRet == -1)  
                throw new IllegalStateException();  
                    checkForComodification();  
          
                try {  
                AbstractList.this.remove(lastRet);  
                if (lastRet < cursor)  
                    cursor--;  
                lastRet = -1;  
                expectedModCount = modCount;//迭代器进行删除时,会改变it的版本  
                } catch (IndexOutOfBoundsException e) {  
                throw new ConcurrentModificationException();  
                }  
            }  
            //检查迭代器的版本与集合的版本是否一致,不同则抛出异常  
            final void checkForComodification() {  
                if (modCount != expectedModCount)  
                throw new ConcurrentModificationException();  
            }  
         }  

    需要知道两点:

    1.modCount,每一次list改变都会进行使其+1

    2.cusor,光标,新遍历元素时候+1

    该案列中,list.size()=4,遍历集合时创建集合迭代器,此时有 expectedModCount = modCount=4,迭代器的cursor=0,

    1cursor=0,it.next()检查版本号一致,遍历第一个元素,cursor=cursor+1=1,打印,然后it.hasNext()函数判断cursor!=list.size(),返回true

    (2cursor=1,it.next()检查版本号一致,遍历第二个元素,cursor=cursor+1=2,打印,然后it.hasNext()函数判断cursor!=list.size(),返回true

    (3cursor=2,it.next()检查版本号一致,遍历第三个元素,cursor=cursor+1=3,进入if语句,移除一个元素,此时集合内容改变,版本号modCount++,5.然后it.hasNext()判断cursor(2)!=size(4),true

    3cursor=3,it.next()检查发现版本号不一致expectedModCount! = modCount,抛出并发修改异常。

    而我们所举的第二个例子可以看出,若要删除的元素位置在倒数第二个中,则遍历会提前结束,原因:

    (1cursor=0,it.next()检查版本号一致,遍历第一个元素,cursor=cursor+1=1,打印,然后it.hasNext()函数判断cursor!=list.size(),返回true

    (2cursor=1,it.next()检查版本号一致,遍历第二个元素,cursor=cursor+1=2,打印,然后it.hasNext()函数判断cursor!=list.size(),返回true

    (3cursor=3,it.next()检查版本号一致,遍历到要删除的元素,cursor=cusor+1=3;进入if语句,list.remove(),此时,版本号modCount++,且集合大小改变list.size()=3,然后,it.hasNext()判断发现cursor(3)==size(3),返回false,迭代提前结束

    因此不会爆出异常

  • 相关阅读:
    第一个SWT程序
    稀疏数组
    算法与数据结构
    《Java核心技术》学习笔记 第1-3章
    算术运算符
    5.11 rw zip file
    5.10 gob序列化
    5.9 piping between writer and reader
    5.7 io.MultiWriter(buf, f)
    5.7 读写 二进制数据
  • 原文地址:https://www.cnblogs.com/masteryellow/p/8661751.html
Copyright © 2011-2022 走看看