ArrayList循環遍歷並刪除元素的幾種情況


如下代碼,想要循環刪除列表中的元素b,該怎么處理?

public class ListDemo {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("a");
        arrayList.add("b");
        arrayList.add("b");
        arrayList.add("c");
        arrayList.add("d");
        arrayList.add("e");
        remove(arrayList);
//        remove2(arrayList);
//        remove3(arrayList);
        System.out.println(arrayList);
    }
}

方法一:for循環遍歷

       public static void remove(ArrayList<String> list) {
            for (int i = 0; i < list.size(); i++) {
                String s = list.get(i);
                if (s.equals("b")) {
                    list.remove(s);
                }
            }
        }

輸出結果:

[a, b, c, d, e]

由結果可知,第二個元素b並未刪除,原因是當第一個元素b被刪除后,它后面所有的元素都向前移動了一個單位,循環時導致第二個元素b漏掉了(本例中從下標2變為了下標1,而下標1已經遍歷過了),可以通過源碼來看:

    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;
    }

進入 fastRemove方法:

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
// 被刪除元素后面所有的元素都向前移動 System.arraycopy(elementData, index
+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }

方法二:foreach循環

        public static void remove2(ArrayList<String> list) {
            for (String s : list) {
                if (s.equals("b")) {
                    list.remove(s);
                }
                System.out.println(s);
            }
        }

會報錯:java.util.ConcurrentModificationException。這是因為在這里,foreach循環遍歷容器本質上是使用迭代器進行遍歷的,會對修改次數modCount進行檢查,不允許集合進行更改操作,源碼如下:

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

方法三:使用迭代器遍歷刪除

        public static void remove3(ArrayList<String> list) {
            Iterator<String> it = list.iterator();  
            while (it.hasNext()) {  
                String s = it.next();  
                if (s.equals("b")) {  
                    it.remove();  
                }  
            }  
        }

使用迭代器遍歷刪除時,能夠避免方法二中出現的問題。這是因為:在ArrayList中,modCount是指集合的修改次數,當進行add或者delete時,modCount會+1;expectedModCount是指集合的迭代器的版本號,初始值是modCount,但是當集合進行add或者delete操作時,modCount會+1,而expectedModCount不會改變,所以方法二中會拋出異常。但是it.remove操作時,會同步expectedModCount的值,把modCount的值賦予expectedModCount。所以不會拋出異常。it.remove的源碼如下:

        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();
            }
        }

總結:如果想正確的循環遍歷刪除元素,需要使用方法三,也就是迭代器遍歷刪除的方法。


免責聲明!

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



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