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。具體原因下篇博客介紹。

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM