Listコレクション削除要素の正しい姿勢

25870 ワード

一、集合要素遍歴
集合を巡回する場合、ビジネスのニーズに応じて集合の要素を排除する必要がある場合があります.通常、リスト集合を巡回するには、次の3つの方法が一般的です.1. for

forint i= 0; i<list.size(); i++) {
}
2. for
forint i : list){
}
3.
Iterator <Integer> iterator = list.iterator();
while (iterator.hasNext()){}

二、集合要素の削除


上の3つの方法を総合して削除をテストします.コードは以下の通りです.
 @Test
    public void test3(){

        try {
            delete1();
        }catch (Exception e){
            log.info("delete1  , {}",e.getClass().getSimpleName());
        }
        try {
            delete2();
        }catch (Exception e){
            log.info("delete2  , {}",e.getClass().getSimpleName());
        }
        try {
            delete3();
        }catch (Exception e){
            log.info("delete3  , {}",e.getClass().getSimpleName());
        }

    } 
    //  list 
    public List<Integer> init(){
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        return  list;
    }
    //  for 
    public <T> void  delete1(){
        List<Integer> list = init();
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(3)){
                list.remove(i);
            }
        }

        log.info("delete1  ");
    }
	//  
    public void  delete2(){
        List<Integer> list = init();
        Iterator <Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            if (next.equals(3)) {
                iterator.remove();
            }
        }
        log.info("delete2  ");
    }
    //  for 
    public  void  delete3(){
        List<Integer> list = init();
        for (Integer t : list) {
            if (t.equals(3)) {
                list.remove(t);
            }
        }
        log.info("delete3  ");
    }


コンソール出力の結果を見てみましょう.
21:03:01.697 [main] INFO com.kuake.concurrent.DemoTest - delete1  
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete2  
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete3  , ConcurrentModificationException

三、テスト結果


>通常for削除:削除OK>強化for削除:削除時に例外を投げ出すConcurrentModificationException>反復器削除:削除OK
四、異常探究
なぜ増強forサイクルを使用するとこの異常が放出されるのか.まず、強化for遍歴コンパイル後のコードを見てみましょう.対応するclassファイルをideaで見つけます.
 public void delete3() {
        List<Integer> list = this.init();
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            Integer t = (Integer)var2.next();
            if (t.equals(3)) {
                list.remove(t);
            }
        }

        log.info("delete3  ");
    }


もともと私たちの反復器は、コンパイル後、jvmもそれを反復器を使って遍歴するように翻訳しました.では、反復器が遍歴したときに何か要求があったのではないかと疑うことができます.ArrayListの反復器ソースコードを参照すると、次のコードが表示されます.コードの断片は以下の通りです.これは彼の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];
        }

上記の方法の1つであるcheckForComodification()コードは以下の通りである.
  final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

modCountはlist集合の修正回数を表し、expectedModCountは合理的な操作時の期限切れの修正回数である.通常動作時modCount=expectedModCount
**理由:**list.remove()メソッドがmodCount++を操作するためです.expectedModCountは元の値を保存します.その後、反復器のnext()操作を実行すると、上記の異常が放出されます.

五、反復器の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();
            }

反復器のremoveメソッドはexpectedModCount = modCount操作を実行します.したがって,next()メソッドを次回実行すると異常は放出されない.

六、まとめ


反復器のremove()メソッドを使用して、集合要素の削除操作を推奨します.