zoukankan      html  css  js  c++  java
  • ArrayList中modCount的作用

    在ArrayList中有个成员变量modCount,继承于AbstractList。

    这个成员变量记录着集合的修改次数,也就每次add或者remove它的值都会加1。这到底有什么用呢?

    先看下面一段测试代码:

    package temp;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Iterator;
    public class demo {
        public static void main(String[] args){
              List<String> list = new ArrayList<String>();
                //CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
                list.add("a");
                Iterator iterator = list.iterator();
                while(iterator.hasNext()){
                    String str = (String) iterator.next();
                    list.remove(str);
                }
        }
    }

    在使用迭代器遍历集合的时候同时修改集合元素。因为ArrayList被设计成非同步的,所以理所当然会抛异常。但是该抛什么异常才能说明该问题呢?

    首先得了解ArrayList的迭代器

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

    在调用list.iterator()的时候返回的是一个Itr对象,它是ArrayList中的一个内部类。

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

    主要关注3个点:

    1、expectedModCount的初值为modCount

    2、hasNext的判断条件为cursor!=size,就是当前迭代的位置不是数组的最大容量值就返回true

    3、next和remove操作之前都会先调用checkForComodification来检查expectedModCount和modCount是否相等

      如果没checkForComodification去检查expectedModCount与modCount相等,这个程序肯定会报ArrayIndexOutOfBoundsException

      这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该

     

    考虑的),为了避免出现这样的异常,定义了检查。所以抛出ConcurrentModificationException异常更能说明问题。

    将测试代码改成如下:

    package temp;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Iterator;
    public class demo {
        public static void main(String[] args){
              List<String> list = new ArrayList<String>();
                //CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
                list.add("a");
                list.add("b");
                list.add("c");
                list.add("d");
                list.add("e");
                Iterator iterator = list.iterator();
                while(iterator.hasNext()){
                    String str = (String) iterator.next();
                    if(str.equals("d")){
                        list.remove(str);
                    }else{
                        System.out.println(str);
                    }
                }
        }
    }

    输出却是 a b c。

      因为在删除 d 的时候cursor为4,size也变成了4。所以hasNext就返回为true了,循环结束,从而后面的元素也不会输出了。

    又想,为什么不把hasNext()的判断改为cursor <=size()呢?但是我们还有可能 add()这样的话就会导致数据混乱,事实上线程安全本身就不允许读的时候被修改。

    这种问题在多线程情况下,操作同一集合很容易暴露,就算改成同步的Vector问题还是会存在,需要使用CopyOnWriteArrayList。具体原因下篇博客介绍。

     

      

  • 相关阅读:
    Android studio快捷键大全 和 eclipse对照(原)
    .net 提取注释生成API文档 帮助文档
    查看443端口被占用无法启动解决办法
    关于正则表达式 C#
    关于 ImageLoader 说的够细了。。。
    什么时候用Application的Context,什么时候用Activity的Context
    关于layoutparam 请铭记。。。。
    java 静态方法上的泛型
    让多个Fragment 切换时不重新实例化
    开源.net 混淆器ConfuserEx介绍
  • 原文地址:https://www.cnblogs.com/zuochengsi-9/p/7050351.html
Copyright © 2011-2022 走看看