foreachはlistを巡って要素を削除すると必ずエラーが表示されますか?

4911 ワード

foreachはlistコレクションを巡っていくつかの要素を削除すると必ずエラーが表示されますか?さあ、前のコードに進みます.
     
1)誤報
 List list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        for (String item : list) {
            if (item.equals("3")) {
                System.out.println(item);
                list.remove(item);
            }
        }
        System.out.println(list.size());

当然、コンソールはjavaを楽しく報告した.util.ConcurrentModificationException.
これはどういうことなのか、それからこの異常を見に行って、自分がやはり若すぎることに気づいた.
forループ、すなわちforeachループを追加することは、リストオブジェクトに基づいてiterator反復オブジェクトを作成することであることが知られています.この反復オブジェクトを使用してlistを遍歴することは、listオブジェクトの要素の遍歴がiteratorに管理されていることに相当します.listを削除するには、iteratorを経由する必要があります.
foreachサイクルのたびに、次の2つの操作があります.
1.iterator.hasNext();//次の要素があるかどうかを判断します.
2.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();
        }

checkForComodification()メソッドに入ったときにエラーが表示されます.つまりmodCount!=expectedModCount.具体的には,foreach方式で要素を遍歴する場合,iteratorを生成し,iterator遍歴を用いる.iteratorを生成するときにexpectedModCountパラメータが保存されます.これはiteratorを生成するときにリストで要素を変更した回数です.遍歴中に要素を削除すると、ListではmodCountが変化し、このmodCountとexceptedModCountが一致しないと異常が放出されます.これは安全のためです.リストのremoveソースを見てください.
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

見て、expectedModCountに対していかなる修正を行っていないで、expectedModCountとmodCountが一致しないことを招いて、異常を投げ出します.したがって、list削除要素を巡回するには、次のようにIteratorを使用します.
Iterator it = list.iterator();
        while(it.hasNext()){
        	if(it.next().equals("3")){
        		it.remove();
        	}
        }

Iteratorのremove()メソッドのソースコードを見てみましょう.expectedModCountには、次のように割り当てられています.
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();


            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;//  expectedModCount
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

これでexpectedModCount=modCountを等しくしておけばエラーは発生しません.
2)foreachのすべてのリスト削除操作でこのエラーが報告されるのではないでしょうか
削除された要素が最後から2番目の数であれば、実は間違いを報告しないことに気づいたかどうか、なぜか、一緒に見てみましょう.
前にforeachループは2つの方法hasNext()とnext()を歩きますと言いました.間違いを報告したくなければnext()メソッドに入らなければ良いのでhasNext()のメソッドを見てみましょう.
public boolean hasNext() {
       return cursor != size;
}

ではhasNext()のメソッドにfalseを返すように要求します.すなわちcursor==sizeです.ここでcursorは、現在のiteratorの位置情報を0から保存するためのItrクラス(Iteratorサブクラス)のフィールドです.cursor自体がカーソルという意味で、データベースの操作で使われることが多い.curosrがsizeに等しくない限り要素が存在すると考えられる.ItrはArrayListの内部クラスであるため、直接ArrayListのsizeフィールドが呼び出されるので、このフィールドの値は動的に変化しており、動的に変化している以上問題が発生する可能性がある.
上記のコードを例にとると、最後から2番目のデータである「4」までの間、cursorは4であり、削除操作が呼び出されるとsizeは5から4になり、hasNext判定が呼び出されると、cursor=sizeとなり、後の操作が呼び出されてそのままループを終了する.上のコードに1行のコードを追加して効果を表示できます.
 for (String item : list) {
        	System.out.println(item);
            if (item.equals("4")) {
                list.remove(item);
            }
 }

出力は:1 2 3 4
これによりhasNext()メソッドを実行して終了し,後に進む異常も見られなくなる.
これにより,foreachでlist要素を削除した場合,最後から2番目の要素だけを削除してもエラーは報告されず,他はエラーが報告されるのでIteratorを使用したことが分かる.
never too late! 参照先:http://rongmayisheng.com/post/%E7%A0%B4%E9%99%A4%E8%BF%B7%E4%BF%A1java-util-arraylist%E5%9C%A8foreach%E5%BE%AA%E7%8E%AF%E9%81%8D%E5%8E%86%E6%97%B6%E5%8F%AF%E4%BB%A5%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0
http://blog.csdn.net/Jywangkeep_/article/details/48754189